updating to version Harmony version 3.1
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Sun, 21 Jun 2026 13:58:34 +0000 (13:58 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Sun, 21 Jun 2026 13:58:34 +0000 (13:58 +0000)
71 files changed:
.gitignore
RT-style-JS_public.tar [deleted file]
administrator/authored/.gitkeep [new file with mode: 0644]
administrator/document/00_Project_Structure.html [deleted file]
administrator/document/01_Workflow_Build_Contract.html [deleted file]
administrator/document/Harmony.md [deleted file]
administrator/document/deprecated/00_Project_Structure.org [deleted file]
administrator/document/deprecated/01_Workflow_and_Build_Contract.org [deleted file]
administrator/document/deprecated/02_RT_Code_Format.org [deleted file]
administrator/document/deprecated/03_Naming_and_Directory_Conventions.org [deleted file]
administrator/document/deprecated/04_Language_Addenda.org [deleted file]
administrator/document/how-to_release.html [new file with mode: 0644]
administrator/document/setup.js
administrator/tool/after_pull [deleted file]
administrator/tool/install_Python [new file with mode: 0755]
administrator/tool/new-project [new file with mode: 0755]
administrator/tool/release [deleted file]
developer/authored/RT/document/RT [deleted symlink]
developer/authored/RT/document/RT0 [new symlink]
developer/document/.gitkeep [deleted file]
developer/document/00_RT_Code_Format.html [deleted file]
developer/document/02_RT_Code_Format.html [deleted file]
developer/document/03_Naming_and_Directory_Conventions.html [deleted file]
developer/document/04_Language_Addenda.html [deleted file]
developer/document/RT-code-format-Lisp.html [new file with mode: 0644]
developer/document/RT-code-format.html [new file with mode: 0644]
developer/document/naming_file-and-directory.html [new file with mode: 0644]
developer/document/single-file_C-module-and-namespace.html [new file with mode: 0644]
developer/tool/do-all [new file with mode: 0755]
developer/tool/make
developer/tool/makefile
developer/tool/release [deleted file]
document/introduction_Harmony.html [new file with mode: 0644]
document/role-and-workflow_product-development.html [new file with mode: 0644]
document/role-and-workflow_product-maintenance.html [new file with mode: 0644]
document/todo.txt [new file with mode: 0644]
fix.py [new file with mode: 0755]
next-generation-name.py [new file with mode: 0755]
setup
shared/authored/.gitkeep [new file with mode: 0644]
shared/authored/scratchpad [deleted file]
shared/authored/setup [deleted file]
shared/authored/style/.gitkeep [deleted file]
shared/authored/version [deleted file]
shared/document/install_Python.org [deleted file]
shared/document/install_generic.org [deleted file]
shared/document/installation_Python.org [new file with mode: 0644]
shared/document/installation_generic.org [new file with mode: 0644]
shared/document/setup.js
shared/style_directory_dict.js [deleted file]
shared/tool/PATH.sh [new file with mode: 0644]
shared/tool/RT-formatter [new file with mode: 0644]
shared/tool/RT-formatter.el [new file with mode: 0644]
shared/tool/env_to_emacs [new file with mode: 0755]
shared/tool/link-RT [new file with mode: 0755]
shared/tool/makefile/Harmony.mk [new file with mode: 0644]
shared/tool/makefile/RT_global.h [new file with mode: 0644]
shared/tool/makefile/target_kernel-module.mk [new file with mode: 0644]
shared/tool/scratchpad [new file with mode: 0755]
shared/tool/setup [new file with mode: 0644]
shared/tool/version [new file with mode: 0755]
shared/upstream/.gitignore [new file with mode: 0644]
temp.sh [deleted file]
tester/RT-formatter/RT-formatter [new file with mode: 0644]
tester/RT-formatter/RT-formatter.el [new file with mode: 0644]
tester/RT-formatter/RT-formatter_alt.el [new file with mode: 0644]
tester/RT-formatter/RT-formatter_script.el [new file with mode: 0644]
tester/RT-formatter/RT-formatter_with-compare [new file with mode: 0644]
tester/RT-formatter/RT-formatter_with-compare.el [new file with mode: 0644]
tester/RT-formatter/data_test-0.c [new file with mode: 0644]
tester/RT-formatter/data_test-1.py [new file with mode: 0644]

index a20243b..5194c58 100644 (file)
@@ -1,3 +1,10 @@
+# Protect against accidental local .gitignore deletion
+**/scratchpad/*
+!**/scratchpad/.gitignore
+
+# so that .gitignore is not required in consumer/release
+/consumer/made
+
 # Python
 __pycache__/
 *.py[cod]
@@ -6,10 +13,14 @@ __pycache__/
 .ipynb_checkpoints/
 .pytest_cache/
 
-# editor backup files (optional)
+# C / compiled artifacts
+*.o
+*.a
+*.out
+*.so
+
+# OS and editor backup files
 *~
 *.bak
-
-# so that .gitignore is not required in /consumer/release
-/consumer/release
-
+*.swp
+.DS_Store
diff --git a/RT-style-JS_public.tar b/RT-style-JS_public.tar
deleted file mode 100644 (file)
index 3967e2d..0000000
Binary files a/RT-style-JS_public.tar and /dev/null differ
diff --git a/administrator/authored/.gitkeep b/administrator/authored/.gitkeep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/administrator/document/00_Project_Structure.html b/administrator/document/00_Project_Structure.html
deleted file mode 100644 (file)
index b087d29..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8">
-    <title>Project Structure and Ontology</title>
-    <script src="setup.js"></script>
-    <script>
-      window.StyleRT.include('RT/theme');
-      window.StyleRT.include('RT/layout/article_tech_ref');
-    </script>
-  </head>
-  <body>
-    <RT-article>
-      <RT-title 
-        author="Harmony Project Team" 
-        date="2026-03-06" 
-        title="Project Structure and Ontology">
-      </RT-title>
-
-      <RT-TOC level="1"></RT-TOC>
-
-      <h1>Purpose</h1>
-      <p>
-        Harmony provides a language-agnostic project directory structure and maintenance tools for long-lived, multi-person team software development. The structure exists to enforce:
-      </p>
-      <ol>
-        <li>Clarity about where things live.
-          <ul>
-            <li>Role-based work areas.</li>
-            <li>Separation of skeleton, team member authored, machine-made, and third-party installed software.</li>
-          </ul>
-        </li>
-        <li>A safe, predictable build and release workflow.</li>
-      </ol>
-
-      <h1>Key Concepts</h1>
-      <h2>Created vs. Made</h2>
-      <p>
-        Harmony divides the world into two categories:
-      </p>
-      <ul>
-        <li><strong>Authored:</strong> Human-written (or AI-written) source files, docs, and design notes.</li>
-        <li><strong>Made:</strong> Tool-produced binaries, generated sources, and intermediates.</li>
-      </ul>
-      <p>
-        This separation protects authored material from accidental overwrite and makes build artifacts fully disposable.
-      </p>
-
-      <h2>Semantic Paths</h2>
-      <p>
-        Directory names in Harmony are not decorative. Each directory name is a <RT-term>property</RT-term> shared among files. A full path forms a semantic sentence describing those files.
-      </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>.
-      </p>
-
-      <ul>
-        <li><RT-code>administrator/</RT-code> — Project-local tools and skeleton maintenance.</li>
-        <li><RT-code>developer/</RT-code> — Primary workspace for developers.</li>
-        <li><RT-code>tester/</RT-code> — Regression and validation workspace for testers.</li>
-        <li><RT-code>consumer/</RT-code> — Consumption workspace holding the <RT-code>release</RT-code> directory.</li>
-        <li><RT-code>shared/</RT-code> — Shared ecosystem tools and global environments.</li>
-      </ul>
-
-      <h1>The Consumer Tree</h1>
-      <p>
-        The <RT-code>consumer/release/</RT-code> tree is where developers put work product that is ready to be consumed. The entire <RT-code>consumer/release</RT-code> directory is git-ignored and treated as a transient deployment target.
-      </p>
-      <p>
-        Artifacts arrive in the <RT-code>release/</RT-code> tree <em>only</em> when the developer invokes the <RT-code>release</RT-code> script, which performs a flat-copy from the developer's <RT-code>scratchpad/stage</RT-code> directory.
-      </p>
-
-      <h1>The Developer Tree</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>.
-      </p>
-      <ul>
-        <li><RT-code>authored/</RT-code> — Human-written source. Tracked by Git.</li>
-        <li><RT-code>made/</RT-code> — Tracked artifacts generated by tools (e.g., links to CLI entry points).</li>
-        <li><RT-code>experiment/</RT-code> — Try-it-here code. Short-lived spot testing.</li>
-        <li><RT-code>scratchpad/</RT-code> — Git-ignored directory. Holds all intermediate build outputs, including the <RT-code>stage</RT-code> directory for releases.</li>
-        <li><RT-code>tool/</RT-code> — Developer-specific tools (like the <RT-code>release</RT-code> and <RT-code>make</RT-code> scripts).</li>
-      </ul>
-
-    </RT-article>
-  </body>
-</html>
diff --git a/administrator/document/01_Workflow_Build_Contract.html b/administrator/document/01_Workflow_Build_Contract.html
deleted file mode 100644 (file)
index a4542a4..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8">
-    <title>Workflow and Build Contract</title>
-    <script src="setup.js"></script>
-    <script>
-      window.StyleRT.include('RT/theme');
-      window.StyleRT.include('RT/layout/article_tech_ref');
-    </script>
-  </head>
-  <body>
-    <RT-article>
-      <RT-title 
-        author="Harmony Project Team" 
-        date="2026-03-05" 
-        title="Workflow and Build Contract">
-      </RT-title>
-
-      <RT-TOC level="1"></RT-TOC>
-
-      <h1>Roles and Process</h1>
-
-      <h2>Administrator Role</h2>
-      <p>Responsibilities:</p>
-      <ol>
-        <li>Setup the project directory and keep it in sync with the Harmony skeleton.</li>
-        <li>Maintain the role environments, apart from the <RT-code>&lt;role&gt;/tool/setup</RT-code> files which are owned by the respective role.</li>
-        <li>Install and maintain shared tools.</li>
-      </ol>
-
-      <h2>Developer Role</h2>
-      <p>Responsibilities:</p>
-      <ol>
-        <li>Write and modify <RT-code>authored/</RT-code> source.</li>
-        <li>Run builds and stage artifacts in <RT-code>scratchpad/stage</RT-code>.</li>
-        <li>Spot test in <RT-code>experiment/</RT-code>.</li>
-        <li>Execute the <RT-code>release write</RT-code> script to copy artifacts to <RT-code>consumer/release</RT-code> for consumption/testing.</li>
-      </ol>
-
-      <h2>Tester Role</h2>
-      <p>Responsibilities:</p>
-      <ol>
-        <li>Validate candidates under <RT-code>consumer/release/</RT-code>.</li>
-        <li>Run regression suites.</li>
-        <li>Approve for quality and completeness, and create release branches.</li>
-      </ol>
-
-      <h1>Entering the Project</h1>
-      <p>
-        To enter a project, cd to the top-level directory and source the setup file for your desired role:
-      </p>
-      <RT-code>
-. setup administrator
-. setup developer
-. setup tester
-. setup consumer
-      </RT-code>
-      <p>
-        It is common to have multiple terminal sessions or IDEs open, each running under a different role environment.
-      </p>
-
-      <h1>Release Promotion</h1>
-      <p>
-        Building and promotion are separate activities. The developer compiles and stages files in <RT-code>developer/scratchpad/stage</RT-code>. The developer then runs <RT-code>release write</RT-code> to transfer those files to <RT-code>consumer/release</RT-code>. 
-      </p>
-      <p>
-        The <RT-code>consumer/release</RT-code> directory is strictly an untracked deployment target. No tools may rebuild during promotion, and no builds are run directly inside the release directory.
-      </p>
-    </RT-article>
-  </body>
-</html>
diff --git a/administrator/document/Harmony.md b/administrator/document/Harmony.md
deleted file mode 100644 (file)
index 45fb023..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-# Harmony — RT project skeleton
-
-Tiny, opinionated starter project skeleton that we use across RT projects independent of language being coded.
-
-Pick a role, source the env, build your thing, then release.
-
-## Roles (source these, don’t execute)
-- `env_developer`  — dev workflow
-- `env_tester`     — test + repro
-- `env_toolsmith`  — shared tools + env wiring
-
-Developers work under `developer/`, testers under `tester/`, toolsmiths wire `shared/` and env scripts.
-
-## Layout (why it exists)
-- `document/`                        — project docs (+ RT conventions in org)
-- `developer/`                       — dev code, experiments, dev-specific docs/tools
-- `tester/`                          — tests, fixtures, repro steps
-- `shared/`                     — shared tools/env for all roles
-- `shared/third_party/`         — third-party tools
-- `shared/third_party/python/`  — your venv lives here (not committed)
-- `release/`                         — publishable artifacts
-- `tmp/`                             — scratch (gitignored)
-
-Empty directories are tracked with `.githolder` (kept out of release archives).
-
-## Quick start
-```bash
-# choose a role (must be sourced)
-. setup developer   # or env_tester / env_toolsmith
-
-# create the Python venv under shared/third_party/python/ (literally 'python' instead of 'venv'
-./scripts/python_venv_bootstrap.sh
-
-# re-enter later
-. setup developer
-
-# where used
-
-In public projects, this structure has been used with Python, Java, C, C++, and Lisp projects.
-
-Note the related https://github.com/Thomas-Walker-Lynch/RT-project-share project. It has the generic makefile used on C/C++ projects and other shared tools.  Note the project https://github.com/Thomas-Walker-Lynch/RT_gcc for a more fully featured cpp.  Note the projects https://github.com/Thomas-Walker-Lynch/Mosaic, and https://github.com/Thomas-Walker-Lynch/Mosaic for Java examples of this project skeleton being used for a Java testing and dependency grapph build tool, respectively.
-
diff --git a/administrator/document/deprecated/00_Project_Structure.org b/administrator/document/deprecated/00_Project_Structure.org
deleted file mode 100644 (file)
index dc3acaf..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-#+TITLE: 00 - Project Structure and Ontology
-#+AUTHOR: Harmony Project Team
-#+DATE: 2025-11-21
-#+OPTIONS: toc:2 num:nil
-
-#+HTML_HEAD_EXTRA: <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
-#+HTML_HEAD_EXTRA: <link rel="stylesheet" type="text/css" href="../style/rt_dark_doc.css">
-
-#+BEGIN_EXPORT html
-<div class="page">
-#+END_EXPORT
-
-* 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.  
-1.1. Role based work areas
-1.2. Separation of skeleton, team member authored, machine-made, and third party installed software.
-3. A safe, predictable build and release workflow.  
-
-A newcomer should be able to clone Harmony and understand the entire
-working model in minutes.
-
-To make a new project a toolsmith first clones Harmony, renames it to the project name, resets the history, and disconnects it from the Harmony project. The skeleton of the new project can be kept in sync with the Harmony skeleton by going to a Harmony skeleton clone and running the =Harmony/tool/sync= tool.
-
-Harmony is IDE agnostic. I typically use Emacs as an IDE and encourage its use. Because of this the documents standard format is emacs `.org` format. Files in this format can be exported to other formats, such as HTML. I have also used IntelliJ IDEA and Eclipse with Harmony, though the project skeleton has drifted some since then. I would like to update Harmony to work out of the box with these and other IDEs in the future.
-
-* 1. Key Concepts
-
-** Created vs. Made
-Harmony divides the world into two categories:
-
-- *Created / Authored*  
-   Human-written: source files, docs, design notes.
-
-- *Made*  
-   Tool-produced: binaries, generated sources, intermediates.
-
-This separation protects authored material from accidental overwrite and
-makes build artifacts fully disposable.
-
-** Semantic Paths
-Directory names in Harmony are not decorative.  
-Each directory name is a *property* shared among files. Thus, a full path forms a semantic
-sentence describing said files.
-
-Example:
-
-- =developer/authored/=  
-  “Developer authored code”
-
-- =developer/scratchpad/made/=  
-  “Developer → scratch workspace → tool-made binaries”
-
-Once you learn the ontology, you can infer the meaning of any path.
-
-* Top-Level Repository Layout
-
-The layout below is stable across all Harmony skeleton based projects:
-
-| Directory | Meaning |
-|----------|---------|
-| =developer/= | Primary workspace for developers  |
-| =tester/= | Regression and validation workspace for testers |
-| =tool/= | Project-local tools |
-| =shared/= | Shared ecosystem tools |
-| =document/= | Documentation (local to project) |
-| =release/= | Central Working Point for promoted artifacts |
-| =scratchpad/= | Global scratch (misc experiments) |
-| =env_* = | Role activators |
-
-The `env_*` files prepare PATH, set environment variables, and cd into
-the correct workspace. A team member will source one of the =env_*= files to take on a role in the project. As of this writing the supported roles are: toolsmith, developer, and tester.
-
-* The =release/= tree.
-
-The =release/= tree is where developers put work product that is to be shared with testers.  Once the contents of the =release/= directory are blessed by the tester, the project will be given a release branch, and then the =release/= tree contains the files that are shared with users. Users should not be pulling files from anywhere else in the project tree.
-
-The =release/= tree is owned by the developer. No other role should write into this tree. 
-
-Ideally, artifacts arrive in the =release/= tree *only* then the developer invokes the =promote= tool. And take note, The developer's =promote= script, as initially provided with the Harmony skeleton, has a command for erasing the contents of the release directory.
-
-** =release/made_tracked/=  
-   Architecture-agnostic artifacts. Tracked by Git, comes when the project is cloned. Users update the release directory when on a release branch, by pulling the project.
-
-** =release/made_untracked/=  
-   Architecture-specific artifacts. Directory tracked, contents are git ignored. The contents of this directory are created by running a build after the project is cloned/pulled. This was a compromise to avoid the problem of maintaining architecture and platform specific binaries.
-
-** =release/documnt/=  
-   Documents for users of the code in the release directory.
-
-* The =developer/= tree
-
-This property is set, i.e. this director is entered, by first going to the top level directory of the project, then sourcing the =env_developer= environment file. The developer can hook additional items into the environment by putting them into the =developer/tool/env= file.
-
-** =authored/=  
-   Human-written source. Tools should never delete files in this directory. =authored/= files are tracked.
-
-** =made/=  
-
-   Generated by tools, and the artifacts are tracked. These artifacts
-   are stable across machine architectures. A common item to find in
-   the =developer/made/= directory is a link to a Python program in
-   the =authored/= directory.  When following RT conventions the entry
-   point of command line driven Python files is `CLI.py`, so the link
-   in =developer/made/= gives the program a name. 
-
-** =experiment/=  
-   Try-it-here code. Short-lived. Developers do spot testing here. If tests are to longer lived, they should be moved to the tester role.
-
-** =scratchpad/=  
-   Contents of this directory are git ignored. It is intended to hold all intermediate build outputs, and anything else the developer might consider scratchpad work. 
-
-** =scratchpad/made/=  
-   By RT convention, architecture specific build artifacts are not tracked, but rather are built each time the project is cloned. Such build artifacts are placed in =developer/scratchpad/made= and if they are to be shared with the tester, the release script will release them to =release/made_untracted=.
-
-** =tool/=  
-   Developer specific tools. Additional tools will be found under =shared=.  If the project is not self contained, then yet additional tools might come from the system environment.
-
-* Documents
-
-** =release/document/=  
-   Documentation for users of released code. E.g.s a =man= page, user manual for an application, or a reference manually for a released library.
-
-** =document/=  
-   Project wide documentation for project team members.
-
-** =developer/document/=  
-   Documentation for developers.
-   
-** =tester/document/=
-   Documentation for testers.
-
-** =shared/document/=
-   Documentation on installing the shared tools. Note if a tool has a document directory that remains with the tool.
-   This will typically have a list of tools that need to be installed for the project, and notes to help make installs go more smoothly.
-
-* Tools
-
-** =tool/=
-
-We call the team members who administer the project and install tools the 'toolsmith'.  The top level =tool/= directory holds the toolsmith's tools.
-
-** =shared/=
-
-Shared tools are available to all team members. Those that have been written specifically for the Harmony skeleton or for this project go into the =shared/authored= directory. Note the tool =scratchpad=,  try =scratchpat help=.
-
-Tools installed from third parties go into the git ignored directory =shared/third_party=. 
-
-** =developer/tool=
-
-Developer role specific tools. The =release= script and the RT project shared =make= scripts are found here.
-
-** =tester/tool=
-
-Tester role specific tools.
-
-
-#+BEGIN_EXPORT html
-</div>
-#+END_EXPORT
diff --git a/administrator/document/deprecated/01_Workflow_and_Build_Contract.org b/administrator/document/deprecated/01_Workflow_and_Build_Contract.org
deleted file mode 100644 (file)
index e34bcf6..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-#+TITLE: 01 - Workflow and Build Contract
-#+AUTHOR: RT
-#+DATE: 2025-11-21
-#+OPTIONS: toc:2 num:nil
-
-#+HTML_HEAD_EXTRA: <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
-#+HTML_HEAD_EXTRA: <link rel="stylesheet" type="text/css" href="../style/rt_dark_doc.css">
-
-#+BEGIN_EXPORT html
-<div class="page">
-#+END_EXPORT
-
-* Purpose
-The workflow contract defines the steps from authorship through release of work product.
-
-There are three circular loops. 
-
-In the development loop, developers author code and run experiments, eventually then promoting work product to the =release/= directory.
-
-In the developer tester loop, testers test the promoted release candidates and file issues against them, developers address these, and then promote new release candidates. Or in a tighter version of this loop, a developer with a local copy of this project plays both roles so as to speed the cycle.
-
-In the third loop, the tester finds the release candidates to meet the goals for the release version, and to be of sufficient quality that they create a new release branch.  Released code then has bug reports filed against it. Developers address these and the prior two loops are run until a new release candidate is stable, and a new release branch is made.
-
-Release branches have two integer numbers. The first number is the version of the software, as per the architectural specification. (That specification is placed into the project document directory.) The second number counts the number of times the tester has created a release branch for said version of the software.
-
-The workflow is designed for forward motion of through release numbers, so as to avoid having to maintain older releases separately. It is better to give a customer a new release if a bug must be fixed, even the customer will not pay for the new release, that it is to pay the cost of dealing with multi-release level bug fixes.  However, as each release has its own branch, it is possible to perform multi-release level bug fixes if that is what is required or desired.
-
-* Roles and Process
-
-** Developer Role
-Responsibilities:
-
-1. Write and modify authored source. Ensure code meets RT style (see =02_RT_Code_Format.org=).
-2. Run builds.
-3. Spot testing in =experiment/=
-4. Promotes release candidates for more thorough testing using the customized =prommote= script.
-5. Rinse, lather, repeat.
-
-** Tester Role
-Responsibilities:
-
-1. Validate candidates under =release/=.  
-2. Run regression suites.  
-3. Approve for quality and completeness, and create release branches.
-
-** Toolsmith Role
-Responsibilities:
-
-1. Setup the project directory, and keep the project in sync with the Harmony skeleton.
-2. Maintain the role environments, apart from the =<role>/tool/env= files which are owned by the respective =<role>=.
-3. Install and maintained shared tools, =tool/= and =shared/=, and other tools upon request.
-4. Address issues with project workflow. Propose updates to the Harmony skeleton.
-
-
-* Entering the project
-
-What I do to enter a project is to first run an emacs shell. I cd to the project I want to work on, and then source the =env_toolsmith=, =env_developer=, or =env_tester= file, depending on which role I want to work in. Although sourcing these files affects the environment of the shell I am running, it does not effect the environment of emacs.  Hence after sourcing the environment, I launch an IDE.  This newly launched IDE will have a correct environment.  For myself, these days, that new IDE will be emacs again.
-
-It is common that I will have two or three IDE's (emacs invocations) running side by side, each as different roles.  Then I can write code, spot test it, promote it, then change to the other IDE and run regression tests.  And if it is a phase of the project where tools are in flux, I will use the third IDE for modifying tools.  Hence, as one person I will taken on three roles simultaneously, each in a different IDE.
-
-On a large project, chances are that all team members will be doing something similar to this on their local clones of the project. However, there will be team members concentrating on code development, and others on testing and release.  Early on a toolsmith will setup the project, and then continue to maintain it.
-
-* Developer
-
-** Authoring and Building
-
-Developers write the build fodder files in the =authored/= directory. File name extensions are used to signal to the build tools how the build fodder is to be used.  When the conventional single extension giving the main file type is not enough, two extensions are used.
-
-For example, with the default makefile for C, compiler fodder is found in the =authored/= directory, each file has one these file name extensions:
-
-- CLIs end in =.cli.c=  
-- Libary code source end in =.lib.c=  
-- Kernel module sources are =.mod.c=  
-
-Fodder with the =.cli.c= extension is made into a stand alone executable.
-
-Fodder with =.lib.c= extension is compiled as an object file and added to the =lib<project>.a= archive. The =.cli.c= files are linkedin against said archive.
-
-Build tools never write into the =developer/authored= directory. Build products that are not to be tracked go on the =scratchpad/=. Those that are tracked go into the =developer/made= directory.
-
-It is expected that developers customize and add to the build scripts that come with the Harmony skeleton in order to fit their specific build needs. Note the Ariadne project for complex builds.
-
-** Developer Testing
-
-Spot tests are run in the =experiment/= directory. If the tests grow complex or are to be kept for the long term, move them to the tester environment.
-
-Once the developer finds the edits to be stable he or she can promote them.  The promoted code is referred to as release candidates. Promoted release candidates can then be read by the tester role.
-
-As I mentioned, it is not uncommon for a team member to have two IDEs open, with one being in the developer environment, and one being in the tester environment, and then to bounce back and fourth between them.
-
-Once the release candidate code is stable, the developer can pull the remote repo, address merge conflicts, then push the local repo back. Merge conflicts on tracked release candidates are common as it is a bottleneck point in the code development.
-
-** Promotion for release
-
-As mentioned, files are promoted from the developer environment to the top level =release/= directory by the developer. The developer effects promotion for release by running the customized =developer/tool/promote= script, and then pushing the repository. Only a tester can actually perform a release.
-
-Building and promotion are separate activities.
-
-- No tool may rebuild during promotion.  
-- Promotion is a copy-only operation.  
-- No builds are run in the =release/= directory.
-
-If architecture specific files are to be part of the release, the developer will develop a  =build_untracked= script and promote it into the =release/tool= directory. Then when a user clones a released project, as a second step the user will invoke the =release/tool/build_untracked= script. That script will fill in the =release/made_untracked= directory with code built specifically for the user's platform. 
-
-- =release/documnt/= (documents for those who intend to use the work product)
-- =release/authored=  (interpreter fodder - _none are run directly_)
-- =release/made_tracked/= (pushed to remote, pulled from remote, links into authored scripts)
-- =release/made_untracked/= (local-only)
-- =release/tool/= (=build_untracked= and other tools for maintaining released code)
-
-We chose the 'build after clone' approach over the 'thousand architecture specific binary release directories' approach, because maintaining many architecture release files became a maintenance problem.  Note this new approach requires that third party tools be installed so that the =release/tool/build_untracked= script can run. This is the trade off cost for nothing having the thousand architecture directories. 
-
-A user of the Harmony skeleton is free to customize the promotion tool and go back to multiple architecture specific binary release directories if that is what they want.
-
-Clearly if work product is intended to be distributed to lay users, there must be a deployment step after the release step, but we do not address this in these documents, as it this is not part of Harmony.
-
-
-* Tester
-
-The developer has promoted release candidates to the =release/= directory. He or she claims those represent a complete high quality product at the given release level. The testers are going to prove the developers to be wrong about that claim. If testers can't disprove this claim, the testers will make a release branch at the next minor release number for the given major release version.
-
-- The tester reads the spec, and writes a complete set of feature tests.
-
-- The tester uses the Mosaic test tool, and writes a set of tests, first for the individual functions that make up the program, then for functions in groups.
-
-- The tester accumulates tests for each bug that ever comes back on a release.
-
-- The tester collects tests from the developer when they are offered.
-
-- The tester writes other tests as he or she sees fit.
-
-- When the tests pass, one presumes, the tester will create a release branch.
-
-* Separation of roles.
-
-A tester never patches code in the =developer/= directory, instead the tester files issues. A tester could propose a code fix on another branch, and then point the developers at it in the issue report.
-
-A developer never writes into =tester/=, instead a developer adds to the =experiment/= and  offers to share tests. A developer can propose tests on another branch, and then point testers at it.
-
-It is up the project manager how strict role assignments will be. 
-
-As mentioned before, one person can play multiple roles. For example, it makes perfect sense for a developer with a local copy of the repo, to have an IDE open as a tester, so that he or she can run tests on release candidates before pushing them. However, in when doing this, the test code might be read only. The developer is merely running it and has no plans to push changes to it.
-
-#+BEGIN_EXPORT html
-</div>
-#+END_EXPORT
diff --git a/administrator/document/deprecated/02_RT_Code_Format.org b/administrator/document/deprecated/02_RT_Code_Format.org
deleted file mode 100644 (file)
index e5ced40..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-#+TITLE: 02 - RT Prescriptive Code Format Guide (Version 3)
-#+AUTHOR: Thomas Walker Lynch
-#+DATE: 2025-12-05
-#+OPTIONS: toc:2 num:nil
-
-#+HTML_HEAD_EXTRA: <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
-#+HTML_HEAD_EXTRA: <link rel="stylesheet" type="text/css" href="../style/rt_dark_doc.css">
-
-#+BEGIN_EXPORT html
-<div class="page">
-#+END_EXPORT
-
-* Purpose
-
-The goal is consistency, readability, and predictability across all
-languages and tools.
-
-This document covers:
-
-1. Naming conventions  
-2. Object vs. Instance Nomenclature  
-3. Vertical comma lists  
-4. Enclosure spacing  
-5. Line breaks and indentation  
-6. Cross-language guidance  
-
-* Object vs. Instance Nomenclature
-
-In the RT world, we reserve the word 'object' for its general English meaning, as its technical meaning in programming often causes confusion. When discussing data that is manipulated solely through a defined interface, use the term **instance**.
-
-- **Object:** Anything that can be described or reasoned about. A 'math object' is anything defined using mathematics, and a 'Python object' is anything that can be described with Python syntax.
-- **Instance:** Data that is only accessed or manipulated through a defined interface. This term is used to clearly denote data encapsulation and separation of concerns.
-
-
-* Identifer Naming Conventions
-
-** Identifier Naming
-
-- Types, modules: *PascalCase*  
-- Functions, variables: *snake_case*  
-- Globals: UPPER_SNAKE_CASE  
-
-** Proper Noun and Acronyms
-
-Even in PascalCase and snake_case, they remain capitalized, as per the English language convention.
-
-E.g.s
-
-- IEEE_publication_count
-- person_Sara_novelties_list
-
-
-** Suffix Semantics
-Optionally suffixes are added to variable names to suggest type or interface.
-
-- =*_dp  :: directory path, not specified if relative or absolute
-- =*_dpr :: relative directory path  
-- =*_dpa :: absolute directory path  
-
-- =*_fp  :: file path, not specified if relative or absolute
-- =*_fpr :: relative file path  
-- =*_fpa :: absolute file path  
-
-If the file system node type is not specifically specified
-
-- =*_fs_nod_p  :: file system node path, not specified if relative or absolute
-- =*_fs_nod_pr :: relative file system node path
-- =*_fs_nod_pa :: absolute file system node path
-
-- =*_list= :: generic ordered items
-- =*_seq=  :: ordered items accessed by index
-
-- =*_map=  :: a keyed container
-- =*_dict  :: a keyed container
-
-- =*_count= :: number of elements  
-- =*_flag= :: boolean  
-
-- = *_Type :: names specific type, where the type name is given in PascalCase, as is the norm for types. E.g.s =name_Array= or =name_Map= for the cases that name is an instance of a defined Array or Map type.
-
-Add a container type suffix instead of making variables names plural. For example,
-
-- =name_seq= :: a sequence of zero or more names, used in place of =names=.
-
-  
-* Comma separated list
-
-RT code format treats the comma in a list as belonging to the item that caused the comma to be needed.
-
-** Horizontal Comma List
-
-For lists on a single line, the comma is preceded by a space and abuts the item it follows.
-
-#+BEGIN_SRC c
-  int x ,y ,z;
-#+END_SRC
-
-Note the space before the comma, and the comma abuts the item that caused the comma to be needed. This applies to language statements and data values alike.
-
-** Vertical Comma List
-
-For lists spanning multiple lines, the comma is placed *before* the item on the new line, aligned with the item's indentation.
-
-#+BEGIN_SRC c
-result = some_function(
-  first_argument
-  ,second_argument
-  ,third_argument
-);
-#+END_SRC
-
-Example in Python:
-
-#+BEGIN_SRC python
-items = [
-  first_item
-  ,second_item
-  ,third_item
-]
-#+END_SRC
-
-- Two-space indent.  
-- Comma at column after indentation.  
-- All items aligned except the first, as it does not have a comma before it.
-- This convention works identically across C, Python, Bash arrays, JSON-like data, etc.
-
-* Enclosure Spacing
-
-This rule applies on a line by line basis.
-
-** General Rules
-
-**No Space Between Adjacent Enclosures:** Generally, there is no space between adjacent enclosure punctuation (e.g., `f(g(x))`).
-
-** Single-Level Enclosures
-
-For enclosures that do not contain other enclosures (e.g., a simple `if(condition)`), there is **no space padding** inside the enclosure punctuation.
-
-Conforming:
-
-#+BEGIN_SRC c
-if(condition){
-  do_something();
-}
-#+END_SRC
-Bad, non-conforming:
-
-#+BEGIN_SRC c
-if(condition) {
-  do_something();
-}
-#+END_SRC
-
-Bad, non-conforming:
-
-#+BEGIN_SRC c
-if ( condition ) {
-  do_something ( );
-}
-#+END_SRC
-
-** Multi-Level Enclosures
-
-For enclosures that contain other enclosures (e.g., `if( f(g(x)) )`), one space of padding is applied only to the **level one (outermost)** enclosure punctuation. All other levels follow the single-level rule (no padding).
-
-#+BEGIN_SRC c
-if( f(g(x)) ){
-  do_something();
-}
-#+END_SRC
-
-In this example, the =if= has a three-level enclosure structure. The outermost parentheses of the =if= condition get one space of padding, while the inner parentheses for =f(...)= and =g(...)= get no padding.
-
-** Unmatched Enclosure Punctuation
-
-Format the enclosure punctuation that is present, as though it were matched. Treat an orphaned opening enclosure punctuation as though it were closed at the end of the line. Treat an extraneous closing, as though there were an opening at the beginning of the line.
-
-** Short Stuff Rule
-
-If a statement, such as an =if= block or a loop, can fit on a single line and is shorter than a reasonable line length (e.g., 40-60 characters), it should be kept on a single line without braces.
-
-#+BEGIN_SRC c
-if(x == 0) return;
-#+END_SRC
-
-* Indentation
-
-- Two spaces per indentation level.  
-- Never use tabs.  
-- Nest lines under the syntactic element that opened them.
-
-* Exercises
-
-To ensure a full understanding of the RT code format, please complete the following exercises.
-
-** Exercise 1: Comma and Function Call Formatting
-
-Reformat the following C code snippet to strictly adhere to the RT code format rules. Pay close attention to the horizontal and vertical comma lists, and the enclosure spacing for the function call.
-
-#+BEGIN_SRC c
-void my_function(int a, int b, int c) {
-  int result = calculate_value(a, b, c);
-  printf("Result: %d, a: %d, b: %d, c: %d\n", result, a, b, c);
-}
-
-result = my_function(
-  rediculously_long_first_argument,
-  rediculously_long_second_argument,
-  rediculously_long_third_argument
-);
-#+END_SRC
-
-** Exercise 2: Multi-Level Enclosure and Short Stuff Rule
-
-Reformat the following C code snippet. The `if` statement should use the multi-level enclosure rule, and the `for` loop body should use the short stuff rule.
-
-#+BEGIN_SRC c
-if (check_permissions(user_id, file_path) && is_valid(file_path)) {
-  for (int i = 0; i < 10; i++) {
-    if (i % 2 == 0) {
-      printf("Even: %d\n", i);
-    }
-  }
-}
-#+END_SRC
-
-#+BEGIN_EXPORT html
-</div>
-#+END_EXPORT
diff --git a/administrator/document/deprecated/03_Naming_and_Directory_Conventions.org b/administrator/document/deprecated/03_Naming_and_Directory_Conventions.org
deleted file mode 100644 (file)
index d719d5b..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#+TITLE: 03 - Naming and Directory Conventions
-#+AUTHOR: RT
-#+DATE: 2025-11-21
-#+OPTIONS: toc:2 num:nil
-#+HTML_HEAD_EXTRA: <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
-#+HTML_HEAD_EXTRA: <link rel="stylesheet" type="text/css" href="../style/rt_dark_doc.css">
-
-#+BEGIN_EXPORT html
-<div class="page">
-#+END_EXPORT
-
-A directory name is taken a property for a set of files. Consequently, directory names are rarely plural. E.g. suppose we have a number of test files in a directory.  The directory would be named =test=. As each file in the directory has the property of being a test.
-
-It would be nice if we could attach multiple properties to a file as part of the file system framework, but conventional file systems do not support this.  Consequently, when needed, people add a second property to a file use dot extensions to the file's name. Hence, we get something like =sqrt.c=  in a directory called =source=.  So the first property is that the file is source code, and the second property is that it is C code.
-
-We could extent the dot suffix model of adding a property to file by using multiple dot suffixes. Our C makefile structure makes use of this.
-
-So what is a reasonable primary property for a set of files?  Perhaps:
-
-- Who uses each file with this property.  Home directories are named like this.
-- The role of the people using the file. This is a more generic version of the prior rule. The =developer= and =tester= directories were named in this manner.
-- What program are the files for. Thus we might name a directory a bunch of files for the cc compiler `cc`.
-- The generic category of program said files are for. Thus we end up with directories called =src= or =executable=.
-
-As for the names =src= and =executable= those come from times when almost all programs were compiled. We prefer instead the names =authored=  and =made=.  =authored= files are those written by humans (or these days, perhaps AI), while =made= files are products of tools. For a Python program, we put packages in =authored= with a module called =CLI.py= for the command line interface. Then we link from =made= into =authored= so as to give the program a name.
-
-The RT C coding environment does not use separate source and header files. Instead a variable is set that gates off the implementation if the source code is to be used as a header. Hence, all of our C source fits fine within and =authored= directory.
-
-
-
-#+BEGIN_EXPORT html
-</div>
-#+END_EXPORT
diff --git a/administrator/document/deprecated/04_Language_Addenda.org b/administrator/document/deprecated/04_Language_Addenda.org
deleted file mode 100644 (file)
index b9b4c77..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-#+TITLE: 04 - Language Addenda (C, Python, Bash)
-#+AUTHOR: RT
-#+DATE: 2025-11-21
-#+OPTIONS: toc:2 num:nil
-#+HTML_HEAD_EXTRA: <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
-#+HTML_HEAD_EXTRA: <link rel="stylesheet" type="text/css" href="../style/rt_dark_doc.css">
-
-#+BEGIN_EXPORT html
-<div class="page">
-#+END_EXPORT
-
-
-* Purpose
-The RT code format is language-agnostic, but actual languages differ in
-syntax and constraints.
-
-This document explains how the RT rules are applied in:
-
-1. C
-2. Python
-3. Bash
-
-For each language we answer:
-
-1. What carries over directly from =02_RT_Code_Format.org=.
-2. What must be adapted.
-3. What extra discipline is required.
-
-* 1. C Addendum
-
-** 1.1 Control Structure and File Layout
-
-The detailed RT C file structure is described in the dedicated =
-RT_C_control_structure= document. The core ideas:
-
-1. Each module has an *Interface* section and an *Implementation*
-   section in the same file.
-2. The sections are toggled using preprocessor macros (e.g. =FACE=).
-3. Interface declarations are processed even when included multiple
-   times; the implementation is compiled only when used as an
-   implementation.
-
-This approach:
-
-1. Keeps the interface and implementation in sync.
-2. Avoids maintaining parallel =.h= and =.c= files for each module.
-3. Integrates smoothly with standardized makefiles.
-
-** 1.2 Indentation and Comma Lists
-
-C code follows the RT two-space indentation and vertical comma lists:
-
-#+BEGIN_SRC c
-result = some_function(
-  first_argument
-  ,second_argument_with_longer_name
-  ,third_argument
-);
-#+END_SRC
-
-Rules:
-
-1. Two spaces per block indentation.
-2. The comma starts the line in vertical lists.
-3. Align continuation lines under the first symbol after the equals
-   sign or opening parenthesis when feasible.
-
-** 1.3 Error Handling and Ownership
-
-Guidelines:
-
-1. Functions should document ownership of pointers and lifetimes.
-2. Prefer explicit =*_count= parameters over sentinel values when
-   passing arrays.
-3. Return codes should be consistent (=0= success, non-zero failure) or
-   use clearly documented enums.
-
-* 2. Python Addendum
-
-** 2.1 Indentation and Layout
-
-Python enforces indentation syntactically, so the RT two-space rule
-becomes:
-
-1. Use *two-space indentation* for all Python code, even though four is
-   common in the wider ecosystem.
-2. Vertical comma lists still place the comma at the start of the line,
-   after the indentation.
-
-Example:
-
-#+BEGIN_SRC python
-items = [
-  first_item
-  ,second_item
-  ,third_item
-]
-#+END_SRC
-
-** 2.2 Modules and CLI Separation
-
-Python scripts distinguish between:
-
-1. *Work functions* (importable API).
-2. *CLI entry points* (argument parsing, printing, exit codes).
-
-Pattern:
-
-1. Put reusable logic into functions and classes.
-2. Put argument parsing and =if __name__ == "__main__":= in the CLI
-   section.
-3. Keep side effects out of import time.
-
-** 2.3 Error Handling
-
-1. Raise exceptions for exceptional conditions.
-2. Catch exceptions at the CLI boundary and convert them into user
-   messages and exit codes.
-3. Avoid catching broad =Exception= unless it is immediately converted
-   into a controlled failure.
-
-* 3. Bash Addendum
-
-** 3.1 Shebang and Safety
-
-Bash scripts should start with:
-
-#+BEGIN_SRC sh
-#!/usr/bin/env bash
-set -euo pipefail
-#+END_SRC
-
-Explanation:
-
-1. =-e= :: Exit on error.
-2. =-u= :: Treat unset variables as errors.
-3. =-o pipefail= :: Propagate errors across pipelines.
-
-** 3.2 Functions vs. Top-Level Code
-
-RT-style Bash separates:
-
-1. A small top-level CLI harness (argument parsing, usage, dispatch).
-2. A set of functions that implement the work.
-
-Pattern:
-
-1. Parse arguments into variables.
-2. Call a main function with explicit parameters.
-3. Avoid relying on global mutable state where possible.
-
-** 3.3 Logging and Diagnostics
-
-1. Use =printf= or =echo= for user-facing messages.
-2. Send debug or trace output to stderr (=>&2=).
-3. Make it obvious when the script is changing system state (e.g.
-   mounting, creating users, modifying firewall rules).
-
-* 4. Using the Addenda
-
-When in doubt:
-
-1. Start with =02_RT_Code_Format.org= for the core rules.
-2. Apply the relevant language section here.
-3. If a language requires deviation from the generic rules, document
-   that deviation in this file instead of ad-hoc decisions.
-
-#+BEGIN_EXPORT html
-</div>
-#+END_EXPORT
diff --git a/administrator/document/how-to_release.html b/administrator/document/how-to_release.html
new file mode 100644 (file)
index 0000000..888d414
--- /dev/null
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>Release howto</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="Thomas Walker Lynch" 
+        date="2026-03-10" 
+        title="How to release a project">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <h1>Prerequisites</h1>
+      <p>
+        A release requires a tested promotion candidate. The developer compiles the code and promotes it to the <RT-code>consumer/made</RT-code> directory. The tester validates the candidate. The product manager gives the order to release.
+      </p>
+
+      <h1>Create the release branch</h1>
+      <p>
+        The <RT-term>administrator</RT-term> executes the release. Open a terminal. Enter the project workspace.
+      </p>
+      <RT-code>
+        > . setup administrator
+      </RT-code>
+
+      <p>
+        Switch to the core development branch. Ensure the local repository is current.
+      </p>
+      <RT-code>
+        > git checkout core_developer_branch
+        > git pull
+      </RT-code>
+
+      <p>
+        Create a new branch for the release. Use the major version number.
+      </p>
+      <RT-code>
+        > git checkout -b release_v&lt;major&gt;
+      </RT-code>
+
+      <h1>Issue management and patching</h1>
+      <p>
+        Only critical issues receive patches on a release branch. The product manager defines what is critical. If approved, the release team applies the fix directly to the existing <RT-code>release_v&lt;major&gt;</RT-code> branch.
+      </p>
+      <p>
+        The branch name <RT-code>release_v&lt;major&gt;</RT-code> remains static. The code on the branch is updated. The administrator advances the minor release number in the <RT-code>shared/tool/version</RT-code> file.
+      </p>
+      <p>
+        The administrator tags the new commit with the full version number, such as <RT-code>v&lt;major&gt;.&lt;minor&gt;</RT-code>. Minor versions are edits on the major release branch. They are visible to a person by running the version command.
+      </p>
+      <p>
+        The developer must ensure the fix is also ported to the <RT-code>core_developer_branch</RT-code>. This prevents the defect from reappearing in future major releases.
+      </p>
+
+      <h1>Tag and push</h1>
+      <p>
+        Tag the release with the exact version number.
+      </p>
+      <RT-code>
+        > git tag -a v&lt;major&gt;.&lt;minor&gt; -m "Release v&lt;major&gt;.&lt;minor&gt;"
+      </RT-code>
+
+      <p>
+        Push the branch to the remote repository. Push the tags.
+      </p>
+      <RT-code>
+        > git push origin release_v&lt;major&gt;
+        > git push origin --tags
+      </RT-code>
+
+      <h1>Repository default branch</h1>
+      <p>
+        Set the repository default branch to the new <RT-code>release_v&lt;major&gt;</RT-code> branch on the hosting platform. This ensures a person cloning or pulling the repository receives the most recent major release code by default.
+      </p>
+
+      <h1>Verification</h1>
+      <p>
+        Check the remote repository. Confirm the branch exists. Confirm the tag is visible. Confirm the default branch is updated.
+      </p>
+
+      <h1>Role responsibilities</h1>
+      <ul>
+        <li><strong>Administrator:</strong> Executes the release. Manages branches, tags, and remote pushes. Updates the version file. Sets the repository default branch.</li>
+        <li><strong>Developer:</strong> Compiles and promotes release candidates. Ports release branch fixes back to the core developer branch.</li>
+        <li><strong>Tester:</strong> Validates candidates. Writes a test for every issue in the core developer queue.</li>
+        <li><strong>Product Manager:</strong> Defines critical issues. Orders the release. Participates in release issue triage.</li>
+        <li><strong>Project Manager:</strong> Participates in release issue triage. Assesses impact to the overall project schedule.</li>
+      </ul>
+
+    </RT-article>
+  </body>
+</html>
index 77ec522..de1173d 100644 (file)
@@ -1,4 +1,4 @@
 window.RT_REPO_ROOT = "../../";
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/style_directory_dict.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/loader.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/body_visibility_hidden.js"></script>');
+document.write('<script src="' + window.RT_REPO_ROOT + 'shared/dictionary_style-directory.js"></script>');
+document.write('<script src="' + window.RT_REPO_ROOT + 'shared/linked-project/RT-style-JS_public/consumer/release/RT/core/loader.js"></script>');
+document.write('<script src="' + window.RT_REPO_ROOT + 'shared/linked-project/RT-style-JS_public/consumer/release/RT/core/body_visibility_hidden.js"></script>');
diff --git a/administrator/tool/after_pull b/administrator/tool/after_pull
deleted file mode 100755 (executable)
index 946d9ad..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-"""
-set_project_permissions — normalize a freshly cloned project to Harmony policies.
-
-usage:
-  set_project_permissions [default]
-  set_project_permissions help | --help | -h
-
-notes:
-  • Must be run from the toolsmith environment (ENV=tool/env, ROLE=toolsmith).
-  • Starts at $REPO_HOME.
-  • Baseline is umask-077 congruence:
-      - directories → 0700
-      - files → 0600, but preserve owner-exec (→ 0700 for executables)
-    applied to the entire repo, including release/, EXCEPT:
-      - release/kmod/*.ko → 0440
-  • Skips .git/ and symlinks.
-"""
-
-import os, sys, stat
-
-# Must match shared/authored/env policy:
-DEFAULT_UMASK = 0o077   # reminder only; effective modes below implement 077 congruence.
-
-DIR_MODE_077 = 0o700
-
-def die(msg, code=1):
-  print(f"set_project_permissions: {msg}", file=sys.stderr)
-  sys.exit(code)
-
-def require_toolsmith_env():
-  env = os.environ.get("ENV", "")
-  role = os.environ.get("ROLE", "")
-  if env != "tool/env" or role != "toolsmith":
-    hint = (
-      "This script should be run from the toolsmith environment.\n"
-      "Try:  source ./env_toolsmith   (then re-run: set_project_permissions default)"
-    )
-    die(f"bad environment: ENV='{env}' ROLE='{role}'.\n{hint}")
-
-def repo_home():
-  rh = os.environ.get("REPO_HOME")
-  if not rh:
-    die("REPO_HOME is not set (did you source shared/authored/env?)")
-  return os.path.realpath(rh)
-
-def show_path(p, rh):
-  return p.replace(rh, "$REPO_HOME", 1) if p.startswith(rh) else p
-
-def is_git_dir(path):
-  return os.path.basename(path.rstrip(os.sep)) == ".git"
-
-def file_target_mode_077_preserve_exec(current_mode: int) -> int:
-  # Base 0600, add owner exec if currently set; drop all group/other.
-  target = 0o600
-  if current_mode & stat.S_IXUSR:
-    target |= stat.S_IXUSR
-  return target
-
-def set_mode_if_needed(path, target, rh):
-  try:
-    st = os.lstat(path)
-  except FileNotFoundError:
-    return 0
-  cur = stat.S_IMODE(st.st_mode)
-  if cur == target:
-    return 0
-  os.chmod(path, target)
-  print(f"+ chmod {oct(target)[2:]} '{show_path(path, rh)}'")
-  return 1
-
-def apply_policy(rh):
-  changed = 0
-  release_root = os.path.join(rh, "release")
-  for dirpath, dirnames, filenames in os.walk(rh, topdown=True, followlinks=False):
-    # prune .git
-    dirnames[:] = [d for d in dirnames if d != ".git"]
-
-    # directories: 0700 everywhere (incl. release/)
-    changed += set_mode_if_needed(dirpath, DIR_MODE_077, rh)
-
-    # files: 0600 (+owner exec) everywhere, except release/kmod/*.ko → 0440
-    rel_from_repo = os.path.relpath(dirpath, rh)
-    under_release = rel_from_repo == "release" or rel_from_repo.startswith("release"+os.sep)
-    top_under_release = ""
-    if under_release:
-      rel_from_release = os.path.relpath(dirpath, release_root)
-      top_under_release = (rel_from_release.split(os.sep, 1)[0] if rel_from_release != "." else "")
-
-    for fn in filenames:
-      p = os.path.join(dirpath, fn)
-      if os.path.islink(p):
-        continue
-      try:
-        st = os.lstat(p)
-      except FileNotFoundError:
-        continue
-
-      if under_release and top_under_release == "kmod" and fn.endswith(".ko"):
-        target = 0o440
-      else:
-        target = file_target_mode_077_preserve_exec(stat.S_IMODE(st.st_mode))
-
-      changed += set_mode_if_needed(p, target, rh)
-  return changed
-
-def cmd_default():
-  require_toolsmith_env()
-  rh = repo_home()
-  total = apply_policy(rh)
-  print(f"changes: {total}")
-
-def main():
-  if len(sys.argv) == 1 or sys.argv[1] in ("default",):
-    return cmd_default()
-  if sys.argv[1] in ("help", "--help", "-h"):
-    print(__doc__.strip()); return 0
-  # unknown command → help
-  print(__doc__.strip()); return 1
-
-if __name__ == "__main__":
-  sys.exit(main())
diff --git a/administrator/tool/install_Python b/administrator/tool/install_Python
new file mode 100755 (executable)
index 0000000..bfc18df
--- /dev/null
@@ -0,0 +1,107 @@
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+
+# Enforce execution for this administrative tool
+if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
+  echo "$script_afp:: This script must be executed, not sourced."
+  return 1
+fi
+
+# without this bash takes non-matching globs literally
+shopt -s nullglob
+
+# does not presume sharing or world permissions
+umask 0077
+
+# Ensure required environment variables exist
+if [[ -z "$REPO_HOME" || -z "$PROJECT" ]]; then
+  echo "Error: REPO_HOME or PROJECT variables are undefined. Please source the setup administrator first."
+  exit 1
+fi
+
+echo "Repository Home: $REPO_HOME"
+echo "Project Name: $PROJECT"
+
+# The scratchpad must exist at the top level of the project for scanning clarity
+scratchpad="$REPO_HOME"/scratchpad
+if [[ ! -d "$scratchpad" ]]; then
+  echo "Error: scratchpad directory missing at $scratchpad"
+  exit 1
+fi
+
+#--------------------------------------------------------------------------------
+# Python name/version
+#
+
+version=3.12.3
+Python_dn=Python-"$version"
+tarf="$Python_dn".tgz
+
+# Python install directory, sibling to this project
+Python_da="$(realpath "$REPO_HOME"/../)"/"$Python_dn"
+
+#--------------------------------------------------------------------------------
+
+# Verify if the requested Python version is already built
+if [[ -x "$Python_da/bin/python3" ]]; then
+  echo "Python $version is already built and installed at $Python_da."
+else
+  # Download only if the archive is absent
+  wget -nc -P "$scratchpad" https://www.python.org/ftp/python/"$version"/"$tarf"
+  
+  # Extract into the scratchpad to prevent source and installation collisions
+  source_dn="$scratchpad"/Python-src-"$version"
+  mkdir -p "$source_dn"
+  tar xzf "$scratchpad"/"$tarf" -C "$source_dn" --strip-components=1
+
+  # Explicit error reporting prevents silent administrative failures
+  cd "$source_dn" || { 
+    echo "Error: Failed to change directory to $source_dn. Extraction likely failed."
+    exit 1 
+  }
+
+  # Configure with developer baseline (--enable-shared) required for C extensions
+  LDFLAGS="-Wl,-rpath=$Python_da/lib" ./configure --prefix="$Python_da" --enable-optimizations --enable-shared
+
+  make -j8
+  make install
+fi
+
+# Establish the versioned executable link
+link_dir="$REPO_HOME"/shared/linked-project
+mkdir -p "$link_dir"
+cd "$link_dir" || {
+  echo "Error: Failed to navigate to linked-project directory at $link_dir"
+  exit 1
+}
+
+if [[ ! -L "$Python_dn" ]]; then
+  # The depth accurately escapes linked-project, shared, and REPO_HOME
+  ln -s ../../../"$Python_dn"/ "$Python_dn"
+  echo "Successfully created symlink for $Python_dn"
+else
+  echo "The symlink for $Python_dn already exists."
+fi
+
+# --------------------------------------------------------------------------------
+# Path & Environment Configuration Plugin 
+# --------------------------------------------------------------------------------
+path_sh="$REPO_HOME"/shared/tool/PATH.sh
+touch "$path_sh"
+
+# Define the cluster of variables required by the python plugin environment
+line_env="export VIRTUAL_ENV=\"\$REPO_HOME/shared/linked-project/$Python_dn\""
+line_home="export PYTHON_HOME=\"\$VIRTUAL_ENV\""
+line_unset="unset PYTHONHOME"
+line_path="PATH=\"\$VIRTUAL_ENV/bin:\$PATH\""
+
+# Append the variables sequentially if they are missing from the configuration file
+for line in "$line_env" "$line_home" "$line_unset" "$line_path"; do
+  if ! grep -Fq "$line" "$path_sh"; then
+    echo "$line" >> "$path_sh"
+    echo "Appended plugin configuration line to PATH.sh."
+  else
+    echo "Configuration line already present in PATH.sh."
+  fi
+done
+
diff --git a/administrator/tool/new-project b/administrator/tool/new-project
new file mode 100755 (executable)
index 0000000..58d49e3
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+import sys ,os ,subprocess
+from pathlib import Path
+
+def get_Z_date() -> str:
+  try:
+    res = subprocess.run(["Z" ,"format-%year-%month-%day"] ,capture_output=True ,text=True ,check=True)
+    return res.stdout.strip()
+  except Exception:
+    return "1970-01-01"
+
+def work(project_name: str ,remotes: list[str]) -> int:
+  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()
+  
+  dest_path = Path(project_name)
+  if not dest_path.is_absolute():
+    dest_path = (repo_path.parent / project_name).resolve()
+
+  if dest_path.exists():
+    print(f"Error: Destination {dest_path} already exists." ,file=sys.stderr)
+    return 1
+
+  print(f"Creating new project at: {dest_path}")
+  dest_path.mkdir(parents=True)
+  
+  archive_cmd = "git archive HEAD | tar -x -C " + str(dest_path)
+  res = subprocess.run(archive_cmd ,shell=True ,cwd=str(repo_path))
+  if res.returncode != 0:
+    print("Error extracting archive." ,file=sys.stderr)
+    return 1
+
+  opus_src = dest_path / "0pus_Harmony"
+  opus_dst = dest_path / f"0pus_{dest_path.name}"
+  if opus_src.exists():
+    opus_src.rename(opus_dst)
+
+  version_file = dest_path / "shared" / "tool" / "version"
+  date_str = get_Z_date()
+  version_line = f"echo \"{dest_path.name} v0.1 {date_str}\"\n"
+  
+  if version_file.exists():
+    with open(version_file ,"a" ,encoding="utf-8") as vf:
+      vf.write(version_line)
+  else:
+    with open(version_file ,"w" ,encoding="utf-8") as vf:
+      vf.write(version_line)
+
+  print("Initializing git repository...")
+  subprocess.run(["git" ,"init" ,"-b" ,"core-developer_branch"] ,cwd=str(dest_path) ,check=True)
+  subprocess.run(["git" ,"add" ,"."] ,cwd=str(dest_path) ,check=True)
+  subprocess.run(["git" ,"commit" ,"-m" ,f"Initial commit of {dest_path.name} from Harmony skeleton"] ,cwd=str(dest_path) ,check=True)
+
+  for idx ,remote_url in enumerate(remotes):
+    remote_name = f"remote_{idx}"
+    if "github.com" in remote_url: 
+      remote_name = "github"
+    elif "RT" in remote_url: 
+      remote_name = "RT"
+    elif idx == 0:
+      remote_name = "origin"
+    
+    print(f"Adding remote '{remote_name}': {remote_url}")
+    subprocess.run(["git" ,"remote" ,"add" ,remote_name ,remote_url] ,cwd=str(dest_path) ,check=True)
+
+  print(f"Project '{dest_path.name}' successfully created.")
+  return 0
+
+def CLI(argv=None) -> int:
+  if argv is None:
+    argv = sys.argv[1:]
+
+  if not argv or "-h" in argv or "--help" in argv:
+    print("Usage: new-project <name> [-remote <url>]...")
+    return 0
+
+  project_name = None
+  remotes = []
+  
+  idx = 0
+  while idx < len(argv):
+    arg = argv[idx]
+    if arg == "-remote":
+      if idx + 1 < len(argv):
+        remotes.append(argv[idx+1])
+        idx += 1
+      else:
+        print("Error: -remote requires a URL." ,file=sys.stderr)
+        return 1
+    elif project_name is None:
+      project_name = arg
+    else:
+      print(f"Error: unexpected argument '{arg}'" ,file=sys.stderr)
+      return 1
+    idx += 1
+
+  if not project_name:
+    print("Error: project name is required." ,file=sys.stderr)
+    return 1
+
+  return work(project_name ,remotes)
+
+if __name__ == "__main__":
+  sys.exit(CLI())
diff --git a/administrator/tool/release b/administrator/tool/release
deleted file mode 100755 (executable)
index d72bf60..0000000
+++ /dev/null
@@ -1,291 +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|help|dry write} [DIR]
-  write [DIR]   Writes released files into $REPO_HOME/release. If [DIR] is specified, only writes files found in scratchpad/DIR.
-  clean [DIR]   Remove the contents of the release directories. If [DIR] is specified, clean only the contents of that release directory.
-  ls            List release/ as an indented tree: PERMS  OWNER  NAME (root-level dotfiles printed first).
-  help          Show this message.
-  dry write [DIR]
-                Preview what write would do without modifying the filesystem.
-"""
-
-SETUP_MUST_BE = "developer/tool/setup"
-DEFAULT_DIR_MODE = 0o700  # 077-congruent dirs
-
-def exit_with_status(msg, code=1):
-  print(f"release: {msg}", file=sys.stderr)
-  sys.exit(code)
-
-def assert_env():
-  env = os.environ.get("ENV", "")
-  if env != ENV_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: ENV='{env}'. {hint}")
-
-def repo_home():
-  rh = os.environ.get("REPO_HOME")
-  if not rh:
-    exit_with_status("REPO_HOME not set (did you '. setup developer'?)")
-  return rh
-
-def dpath(*parts):
-  return os.path.join(repo_home(), "developer", *parts)
-
-def rpath(*parts):
-  return os.path.join(repo_home(), "release", *parts)
-
-def dev_root():
-  return dpath()
-
-def rel_root():
-  return rpath()
-
-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, rel_root())
-    rel = "" if rel == "." else rel
-    return "$REPO_HOME/release" + ("/" + rel if rel else "")
-  except Exception:
-    return p_abs
-
-def ensure_mode(path, mode):
-  try: os.chmod(path, mode)
-  except Exception: pass
-
-def ensure_dir(path, mode=DEFAULT_DIR_MODE, dry=False):
-  if dry:
-    if not os.path.isdir(path):
-      shown = _display_dst(path) if path.startswith(rel_root()) else (
-        os.path.relpath(path, dev_root()) if path.startswith(dev_root()) else path
-      )
-      print(f"(dry) mkdir -m {oct(mode)[2:]} '{shown}'")
-    return
-  os.makedirs(path, exist_ok=True)
-  ensure_mode(path, 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}"
-
-# ---------- LS (two-pass owner:group width) ----------
-def list_tree(root):
-  if not os.path.isdir(root):
-    return
-  entries = []
-  def gather(path: str, depth: int, is_root: bool):
-    try:
-      it = list(os.scandir(path))
-    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, depth=1, is_root=True)
-
-  ogw = 0
-  for (_isdir, _depth, _perms, ownergrp, _name) in entries:
-    if len(ownergrp) > ogw: ogw = len(ownergrp)
-
-  print("release/")
-  for (_isdir, depth, perms, ownergrp, name) in entries:
-    indent = "  " * depth
-    print(f"{perms}  {ownergrp:<{ogw}}  {indent}{name}")
-# ---------- end LS ----------
-
-def iter_src_files(topdir, src_root):
-  base = os.path.join(src_root, topdir) if topdir else src_root
-  if not os.path.isdir(base):
-    return
-    yield
-  if topdir == "kmod":
-    for p in sorted(glob.glob(os.path.join(base, "*.ko"))):
-      yield (p, os.path.basename(p))
-  else:
-    for root, dirs, files in os.walk(base):
-      dirs.sort(); files.sort()
-      for fn in files:
-        src = os.path.join(root, fn)
-        rel = os.path.relpath(src, base)
-        yield (src, rel)
-
-def _target_mode_from_source(src_abs: str) -> int:
-  """077 policy: files 0600; if source has owner-exec, make 0700."""
-  try:
-    sm = stat.S_IMODE(os.stat(src_abs).st_mode)
-  except FileNotFoundError:
-    return 0o600
-  return 0o700 if (sm & stat.S_IXUSR) else 0o600
-
-def copy_one(src_abs, dst_abs, 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)
-  target_mode = _target_mode_from_source(src_abs)
-
-  def _is_writable_dir(p): return os.access(p, os.W_OK)
-  flip_needed = not _is_writable_dir(parent)
-  restore_mode = None
-  parent_show = _display_dst(parent)
-
-  if dry:
-    if flip_needed:
-      print(f"(dry) chmod u+w '{parent_show}'")
-    if os.path.exists(dst_abs):
-      print(f"(dry) unlink '{dst_show}'")
-    # show final mode we will set
-    print(f"(dry) install -m {oct(target_mode)[2:]} -D '{src_show}' '{dst_show}'")
-    if flip_needed:
-      print(f"(dry) chmod u-w '{parent_show}'")
-    return
-
-  try:
-    if flip_needed:
-      try:
-        st_parent = os.stat(parent)
-        restore_mode = stat.S_IMODE(st_parent.st_mode)
-        os.chmod(parent, restore_mode | stat.S_IWUSR)
-      except PermissionError:
-        exit_with_status(f"cannot write: parent dir not writable and chmod failed on {parent_show}")
-
-    # Atomic replace with enforced 077-compliant mode
-    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, target_mode)
-      os.replace(tmp_path, dst_abs)
-    finally:
-      try:
-        if os.path.exists(tmp_path):
-          os.unlink(tmp_path)
-      except Exception:
-        pass
-  finally:
-    if restore_mode is not None:
-      try: os.chmod(parent, restore_mode)
-      except Exception: pass
-
-  print(f"+ install -m {oct(target_mode)[2:]} '{src_show}' '{dst_show}'")
-
-def write_one_dir(topdir, dry):
-  rel_root_dir = rpath()
-  src_root = dpath("scratchpad")
-  src_dir = os.path.join(src_root, topdir)
-  dst_dir = os.path.join(rel_root_dir, topdir)
-
-  if not os.path.isdir(src_dir):
-    exit_with_status(
-      f"cannot write: expected '{_display_src(src_dir)}' to exist. "
-      f"Create scratchpad/{topdir} (Makefiles may need to populate it)."
-    )
-
-  ensure_dir(dst_dir, DEFAULT_DIR_MODE, dry=dry)
-
-  wrote = False
-  for src_abs, rel in iter_src_files(topdir, src_root):
-    dst_abs = os.path.join(dst_dir, rel)
-    copy_one(src_abs, dst_abs, dry=dry)
-    wrote = True
-  if not wrote:
-    msg = "no matching artifacts found"
-    if topdir == "kmod": msg += " (looking for *.ko)"
-    print(f"(info) {msg} in {_display_src(src_dir)}")
-
-def cmd_write(dir_arg, dry=False):
-  assert_env()
-  ensure_dir(rpath(), DEFAULT_DIR_MODE, dry=dry)
-
-  src_root = dpath("scratchpad")
-  if not os.path.isdir(src_root):
-    exit_with_status(f"cannot find developer scratchpad at '{_display_src(src_root)}'")
-
-  if dir_arg:
-    write_one_dir(dir_arg, dry=dry)
-  else:
-    subs = sorted([e.name for e in os.scandir(src_root) if e.is_dir(follow_symlinks=False)])
-    if not subs:
-      print(f"(info) nothing to release; no subdirectories found under {_display_src(src_root)}")
-      return
-    for td in subs:
-      write_one_dir(td, dry=dry)
-
-def _clean_contents(dir_path):
-  if not os.path.isdir(dir_path): return
-  for name in os.listdir(dir_path):
-    p = os.path.join(dir_path, 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 cmd_clean(dir_arg):
-  assert_env()
-  rel_root_dir = rpath()
-  if not os.path.isdir(rel_root_dir):
-    return
-  if dir_arg:
-    _clean_contents(os.path.join(rel_root_dir, dir_arg))
-  else:
-    for e in os.scandir(rel_root_dir):
-      if e.is_dir(follow_symlinks=False):
-        _clean_contents(e.path)
-
-def CLI():
-  if len(sys.argv) < 2:
-    print(HELP); return
-  cmd, *args = sys.argv[1:]
-  if cmd == "write":
-    cmd_write(args[0] if args else None, dry=False)
-  elif cmd == "clean":
-    cmd_clean(args[0] if args else None)
-  elif cmd == "ls":
-    list_tree(rpath())
-  elif cmd == "help":
-    print(HELP)
-  elif cmd == "dry":
-    if args and args[0] == "write":
-      cmd_write(args[1] if len(args) >= 2 else None, dry=True)
-    else:
-      print(HELP)
-  else:
-    print(HELP)
-
-if __name__ == "__main__":
-  CLI()
diff --git a/developer/authored/RT/document/RT b/developer/authored/RT/document/RT
deleted file mode 120000 (symlink)
index 2a6dec2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/home/Thomas/subu_data/developer/project/RT-style-JS_public/shared/linked-project/RT-style-JS_public/consumer/release/RT
\ No newline at end of file
diff --git a/developer/authored/RT/document/RT0 b/developer/authored/RT/document/RT0
new file mode 120000 (symlink)
index 0000000..2a6dec2
--- /dev/null
@@ -0,0 +1 @@
+/home/Thomas/subu_data/developer/project/RT-style-JS_public/shared/linked-project/RT-style-JS_public/consumer/release/RT
\ No newline at end of file
diff --git a/developer/document/.gitkeep b/developer/document/.gitkeep
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/developer/document/00_RT_Code_Format.html b/developer/document/00_RT_Code_Format.html
deleted file mode 100644 (file)
index cbb7abc..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8">
-    <title>RT Prescriptive Code Format Guide</title>
-    <script src="setup.js"></script>
-    <script>
-      window.RT.load('theme');
-      window.RT.load('layout/article_tech_ref');
-    </script>
-  </head>
-  <body>
-    <RT-article>
-      <RT-title 
-        author="Thomas Walker Lynch" 
-        date="2026-03-05" 
-        title="RT Prescriptive Code Format Guide (Version 4)">
-      </RT-title>
-
-      <RT-TOC level="1"></RT-TOC>
-
-      <h1>Object vs. Instance Nomenclature</h1>
-      <p>
-        We reserve the word 'object' for its general English meaning. When discussing data that is manipulated solely through a defined interface, use the term <RT-term>instance</RT-term>.
-      </p>
-
-      <h1>Identifier Naming Conventions</h1>
-      <ul>
-        <li>Types, modules: <RT-code>PascalCase</RT-code></li>
-        <li>Functions, variables: <RT-code>snake_case</RT-code></li>
-        <li>Globals: <RT-code>UPPER_SNAKE_CASE</RT-code></li>
-      </ul>
-      <p>
-        <em>Note for Lisp/elisp:</em> Use a hyphen <RT-code>-</RT-code> as the primary identifier separator. Use an underscore <RT-code>_</RT-code> to separate logically related portions of the identifier.
-      </p>
-      <p>
-        <em>Note for C:</em> Use a center dot <RT-code>·</RT-code> for ad hoc namespaces within identifiers (e.g., <RT-code>TM·Arr</RT-code>). In Python, map this to an underscore (<RT-code>TM_Arr</RT-code>).
-      </p>
-
-      <h2>Suffix Semantics</h2>
-      <p>Add a container type suffix instead of making variables names plural. Never use plurals.</p>
-      <ul>
-        <li><RT-code>*_dp</RT-code> : directory path</li>
-        <li><RT-code>*_fp</RT-code> : file path</li>
-        <li><RT-code>*_list</RT-code> : generic ordered items</li>
-        <li><RT-code>*_count</RT-code> : number of elements</li>
-      </ul>
-
-      <h1>Comma Separated Lists</h1>
-      <h2>Horizontal Comma List</h2>
-      <p>The comma is preceded by a space and abuts the item it follows.</p>
-      <RT-code>
-int x ,y ,z;
-      </RT-code>
-
-      <h2>Vertical Comma List</h2>
-      <p>The comma is placed <em>before</em> the item on the new line, aligned with the item's indentation.</p>
-      <RT-code>
-result = some_function(
-  first_argument
-  ,second_argument
-  ,third_argument
-);
-      </RT-code>
-
-      <h1>Enclosure Spacing</h1>
-      <h2>Single-Level Enclosures</h2>
-      <p>No space padding inside the enclosure punctuation.</p>
-      <RT-code>
-if(condition){
-  do_something();
-}
-      </RT-code>
-
-      <h2>Multi-Level Enclosures</h2>
-      <p>One space of padding is applied <em>only</em> to the outermost enclosure punctuation.</p>
-      <RT-code>
-if( f(g(x)) ){
-  do_something();
-}
-      </RT-code>
-
-      <h2>Short Stuff Rule</h2>
-      <p>If a statement can fit on a single line and is short, keep it on a single line without braces.</p>
-      <RT-code>
-if(x == 0) return;
-      </RT-code>
-
-      <h1>Indentation</h1>
-      <ul>
-        <li>Strictly two spaces per indentation level.</li>
-        <li>Never use tabs.</li>
-        <li>Nest lines under the syntactic element that opened them.</li>
-      </ul>
-    </RT-article>
-  </body>
-</html>
diff --git a/developer/document/02_RT_Code_Format.html b/developer/document/02_RT_Code_Format.html
deleted file mode 100644 (file)
index e24f9ec..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8">
-    <title>RT Prescriptive Code Format Guide</title>
-    <script src="setup.js"></script>
-    <script>
-      window.RT.load('theme');
-      window.RT.load('layout/article_tech_ref');
-    </script>
-  </head>
-  <body>
-    <RT-article>
-      <RT-title 
-        author="Thomas Walker Lynch" 
-        date="2026-03-05" 
-        title="RT Prescriptive Code Format Guide (Version 4)">
-      </RT-title>
-
-      <RT-TOC level="1"></RT-TOC>
-
-      <h1>Object vs. Instance Nomenclature</h1>
-      <p>
-        We reserve the word 'object' for its general English meaning. When discussing data that is manipulated solely through a defined interface, use the term <RT-term>instance</RT-term>.
-      </p>
-
-      <h1>Identifier Naming Conventions</h1>
-      <ul>
-        <li>Types, modules: <RT-code>PascalCase</RT-code></li>
-        <li>Functions, variables: <RT-code>snake-kebab_case</RT-code> (if <RT-code>-</RT-code> is supported) or <RT-code>snake_case</RT-code> (if not supported)</li>
-        <li>Globals: <RT-code>UPPER_SNAKE_CASE</RT-code></li>
-      </ul>
-
-      <h2>Primary and Secondary Separators (snake-kebab_case)</h2>
-      <p>
-        If a language supports the hyphen (<RT-code>-</RT-code>) in identifiers (such as Common Lisp and Emacs Lisp), the identifier is written in <RT-code>snake-kebab_case</RT-code>. The hyphen is used as the primary word separator. The underscore (<RT-code>_</RT-code>) is then used as a secondary separator to denote logically related portions or namespace boundaries within the identifier. 
-      </p>
-      <p>
-        Otherwise, if the language does not support hyphens in identifiers (such as C, Python, and Java), the identifier falls back to standard <RT-code>snake_case</RT-code> and only the underscore (<RT-code>_</RT-code>) is used for all separation. In C specifically, a center dot (<RT-code>·</RT-code>) is used for ad hoc namespaces.
-      </p>
-
-      <h2>Suffix Semantics</h2>
-      <p>Add a container type suffix instead of making variable names plural. Never use plurals.</p>
-      <ul>
-        <li><RT-code>*_dp</RT-code> : directory path</li>
-        <li><RT-code>*_fp</RT-code> : file path</li>
-        <li><RT-code>*_list</RT-code> : generic ordered items</li>
-        <li><RT-code>*_count</RT-code> : number of elements</li>
-      </ul>
-
-      <h1>Comma Separated Lists</h1>
-      <h2>Horizontal Comma List</h2>
-      <p>The comma is preceded by a space and abuts the item it follows.</p>
-      <RT-code>
-int x ,y ,z;
-      </RT-code>
-
-      <h2>Vertical Comma List</h2>
-      <p>The comma is placed <em>before</em> the item on the new line, aligned with the item's indentation.</p>
-      <RT-code>
-result = some_function(
-  first_argument
-  ,second_argument
-  ,third_argument
-);
-      </RT-code>
-
-      <h1>Enclosure Spacing</h1>
-      <h2>Single-Level Enclosures</h2>
-      <p>No space padding inside the enclosure punctuation.</p>
-      <RT-code>
-if(condition){
-  do_something();
-}
-      </RT-code>
-
-      <h2>Multi-Level Enclosures</h2>
-      <p>One space of padding is applied <em>only</em> to the outermost enclosure punctuation.</p>
-      <RT-code>
-if( f(g(x)) ){
-  do_something();
-}
-      </RT-code>
-
-      <h2>Short Stuff Rule</h2>
-      <p>If a statement can fit on a single line and is short, keep it on a single line without braces.</p>
-      <RT-code>
-if(x == 0) return;
-      </RT-code>
-
-      <h1>Indentation</h1>
-      <ul>
-        <li>Strictly two spaces per indentation level.</li>
-        <li>Never use tabs.</li>
-        <li>Nest lines under the syntactic element that opened them.</li>
-      </ul>
-    </RT-article>
-  </body>
-</html>
diff --git a/developer/document/03_Naming_and_Directory_Conventions.html b/developer/document/03_Naming_and_Directory_Conventions.html
deleted file mode 100644 (file)
index d1d1851..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8">
-    <title>Naming and Directory Conventions</title>
-    <script src="setup.js"></script>
-    <script>
-      window.RT.load('theme');
-      window.RT.load('layout/article_tech_ref');
-    </script>
-  </head>
-  <body>
-    <RT-article>
-      <RT-title 
-        author="RT" 
-        date="2026-03-05" 
-        title="Naming and Directory Conventions">
-      </RT-title>
-
-      <p>
-        A directory name is taken as a <RT-term>property</RT-term> for a set of files. Consequently, directory names are rarely plural. For example, suppose we have a number of test files in a directory. The directory would be named <RT-code>test</RT-code>, as each file in the directory has the property of being a test.
-      </p>
-
-      <p>
-        It would be nice if we could attach multiple properties to a file as part of the file system framework, but conventional file systems do not support this. Consequently, when needed, people add a second property to a file using dot extensions to the file's name. Hence, we get something like <RT-code>sqrt.c</RT-code> in a directory called <RT-code>source</RT-code>. The first property is that the file is source code, and the second property is that it is C code.
-      </p>
-
-      <p>
-        We could extend the dot suffix model of adding a property to a file by using multiple dot suffixes. Our C makefile structure makes use of this.
-      </p>
-
-      <p>So what is a reasonable primary property for a set of files? Perhaps:</p>
-      <ul>
-        <li>Who uses each file with this property. Home directories are named like this.</li>
-        <li>The role of the people using the file. This is a more generic version of the prior rule. The <RT-code>developer</RT-code> and <RT-code>tester</RT-code> directories were named in this manner.</li>
-        <li>What program are the files for. Thus we might name a directory of files for the C compiler <RT-code>cc</RT-code>.</li>
-        <li>The generic category of program said files are for. Thus we end up with directories called <RT-code>src</RT-code> or <RT-code>executable</RT-code>.</li>
-      </ul>
-
-      <p>
-        As for the names <RT-code>src</RT-code> and <RT-code>executable</RT-code>, those come from times when almost all programs were compiled. We prefer instead the names <RT-code>authored</RT-code> and <RT-code>made</RT-code>. <RT-code>authored</RT-code> files are those written by humans (or these days, perhaps AI), while <RT-code>made</RT-code> files are products of tools. For a Python program, we put packages in <RT-code>authored</RT-code> with a module called <RT-code>CLI.py</RT-code> for the command line interface. Then we link from <RT-code>made</RT-code> into <RT-code>authored</RT-code> so as to give the program a name.
-      </p>
-
-      <p>
-        The RT C coding environment does not use separate source and header files. Instead, a variable is set that gates off the implementation if the source code is to be used as a header. Hence, all of our C source fits fine within an <RT-code>authored</RT-code> directory.
-      </p>
-    </RT-article>
-  </body>
-</html>
diff --git a/developer/document/04_Language_Addenda.html b/developer/document/04_Language_Addenda.html
deleted file mode 100644 (file)
index a4d345c..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8">
-    <title>Language Addenda (C, Python, Bash, Lisp)</title>
-    <script src="setup.js"></script>
-    <script>
-      window.RT.load('theme');
-      window.RT.load('layout/article_tech_ref');
-    </script>
-  </head>
-  <body>
-    <RT-article>
-      <RT-title 
-        author="RT" 
-        date="2026-03-05" 
-        title="Language Addenda (C, Python, Bash, Lisp)">
-      </RT-title>
-
-      <RT-TOC level="1"></RT-TOC>
-
-      <h1>Purpose</h1>
-      <p>
-        The RT code format is language-agnostic, but actual languages differ in syntax and constraints. This document explains how the RT rules are applied in C, Python, Bash, and Lisp dialects.
-      </p>
-
-      <h1>1. C Addendum</h1>
-      <h2>1.1 Control Structure and File Layout</h2>
-      <p>
-        Each module has an <strong>Interface</strong> section and an <strong>Implementation</strong> section in the same file. The sections are toggled using preprocessor macros (e.g., <RT-code>FACE</RT-code>). Interface declarations are processed even when included multiple times; the implementation is compiled only when used as an implementation.
-      </p>
-
-      <h2>1.2 Indentation and Comma Lists</h2>
-      <p>C code follows the RT two-space indentation and vertical comma lists:</p>
-      <RT-code>
-result = some_function(
-  first_argument
-  ,second_argument_with_longer_name
-  ,third_argument
-);
-      </RT-code>
-
-      <h2>1.3 Error Handling and Ownership</h2>
-      <ul>
-        <li>Functions should document ownership of pointers and lifetimes.</li>
-        <li>Prefer explicit <RT-code>*_count</RT-code> parameters over sentinel values when passing arrays.</li>
-      </ul>
-
-      <h1>2. Python Addendum</h1>
-      <h2>2.1 Indentation and Layout</h2>
-      <p>Python enforces indentation syntactically, so the RT two-space rule becomes:</p>
-      <ul>
-        <li>Use <strong>two-space indentation</strong> for all Python code, even though four is common in the wider ecosystem.</li>
-        <li>Vertical comma lists still place the comma at the start of the line, after the indentation.</li>
-      </ul>
-
-      <h2>2.2 Modules and CLI Separation</h2>
-      <p>Python scripts distinguish between:</p>
-      <ol>
-        <li><strong>Work functions</strong> (importable API).</li>
-        <li><strong>CLI entry points</strong> (argument parsing, printing, exit codes).</li>
-      </ol>
-      <p>Put argument parsing and <RT-code>if __name__ == "__main__":</RT-code> in the CLI section. Keep side effects out of import time.</p>
-
-      <h1>3. Bash Addendum</h1>
-      <h2>3.1 Shebang and Safety</h2>
-      <p>Bash scripts should start with:</p>
-      <RT-code>
-#!/usr/bin/env bash
-set -euo pipefail
-      </RT-code>
-
-      <h2>3.2 Functions vs. Top-Level Code</h2>
-      <p>RT-style Bash separates a small top-level CLI harness (argument parsing, usage, dispatch) from a set of functions that implement the work. Parse arguments into variables, call a main function with explicit parameters, and avoid relying on global mutable state where possible.</p>
-
-      <h1>4. Lisp / Emacs Lisp Addendum</h1>
-      <h2>4.1 Identifier Separators (snake-kebab_case)</h2>
-      <p>
-        Because Lisp dialects support the hyphen (<RT-code>-</RT-code>) in symbols, they utilize <RT-code>snake-kebab_case</RT-code> as the standard for functions and variables. The hyphen replaces the underscore as the primary word separator.
-      </p>
-      <p>
-        The underscore (<RT-code>_</RT-code>) is reserved strictly as a secondary separator. It is used to group logically related portions of an identifier or to denote semantic boundaries, acting as a structural namespace within the symbol (e.g., <RT-code>city-scape_building-height</RT-code>).
-      </p>
-      
-      <h2>4.2 Enclosures and Indentation</h2>
-      <p>
-        Lisp relies entirely on parentheses for evaluation boundaries. The RT multi-level enclosure rule applies: one space of padding is applied to the outermost parentheses of an evaluated list, while inner lists receive no padding. Indentation remains strictly two spaces.
-      </p>
-
-      <h1>5. Using the Addenda</h1>
-      <p>
-        When in doubt, start with <RT-code>02_RT_Code_Format.html</RT-code> for the core rules, then apply the relevant language section here. If a language requires deviation from the generic rules, document that deviation in this file instead of ad-hoc decisions.
-      </p>
-    </RT-article>
-  </body>
-</html>
diff --git a/developer/document/RT-code-format-Lisp.html b/developer/document/RT-code-format-Lisp.html
new file mode 100644 (file)
index 0000000..edbed6b
--- /dev/null
@@ -0,0 +1,392 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>RT Code Format: Lisp Addendum</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="Thomas Walker Lynch" 
+        date="2026-03-28" 
+        title="RT Code Format: Lisp Addendum">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <p>
+        This document serves as the authoritative addendum for applying the RT code format conventions to Lisp dialects, specifically Emacs Lisp. It integrates micro-syntax rules with macro-structural organization to ensure consistency across the software ecosystem.
+      </p>
+
+      <h1>File Architecture and Section Banners</h1>
+      <p>
+        To maintain a predictable progression from static data to external hooks, a Lisp file is divided into standard architectural sections. Each section is introduced by an <RT-term>Architectural Banner</RT-term>.
+      </p>
+
+      <ul>
+        <li><strong>Utilities:</strong>General use helpers that are independent of the code in the module. Perhaps these will later be moved to a utilities library.</li>
+        <li><strong>Configuration:</strong> Houses foundational parameters, constants (<RT-code>defconst</RT-code>), and global variables that dictate module behavior.</li>
+        <li><strong>Interior code:</strong> Reserved for internal, non-exported mechanisms. This includes private helper functions, macros, and localized state variables that support the public interface but remain isolated from external dependencies.</li>
+        <li><strong>API:</strong> The public boundary of the module. It contains the primary functions with stable signatures and clear contracts intended for consumption by other parts of the system.</li>
+        <li><strong>Interactive:</strong> Isolates functions designed for direct human execution through the Emacs command loop, typically utilizing the <RT-code>(interactive)</RT-code> declaration.</li>
+        <li><strong>Integration:</strong> Placed at the bottom of the file, this section handles side effects that wire the module into the broader environment, such as adding hooks, defining keybindings, or applying advice. It is permitted to reference the API and Interactive sections, but must never reference Interior code.</li>
+      </ul>
+
+      <h1>Comment Ontology</h1>
+      <p>
+        The commenting style in Lisp is highly structured, using semicolon counts and indentation to create a clear visual hierarchy. A programmer should assume the reader can understand Lisp syntax; avoid comments that merely translate the code below them into English prose.
+      </p>
+
+      <ul>
+        <li><strong>The Prologue Block:</strong> Located at the very top of the document. It uses flush left triple semicolons <RT-code>;;;</RT-code>. Empty triple semicolons act as vertical spacing to separate paragraphs without breaking the block format.</li>
+
+        <li><strong>The Architectural Banner:</strong> Used for major file divisions (like Configuration, API). It consists of flush left triple semicolons followed by a continuous line of dashes, a title line, and an empty triple semicolon line.</li>
+
+        <li><strong>The Scope Header:</strong> Used to group related variables or functions within a major section. These are indented two spaces, begin with double semicolons <RT-code>;;</RT-code>, and are followed by an empty double semicolon line for vertical padding.</li>
+
+        <li><strong>The Contract Annotation:</strong> Double semicolon comments indented two spaces, placed directly above a function definition. They specify preconditions, structural expectations, or size checks that must be true before the function is called.</li>
+
+        <li><strong>The Trailing Tag:</strong> A single semicolon <RT-code>;</RT-code> placed at the end of a line of executable code, used to label a specific return state or note a brief detail.</li>
+      </ul>
+
+      <p>Header case, for all RT documents and code, is an initial capital letter, and no trailing period. Acronyms and tagged code or terms keep their original capitalization or lack thereof.</p>
+
+      <h1>Identifier Naming and Namespaces</h1>
+
+      <h2>Primary and Secondary Separators</h2>
+      <p>
+        Because Emacs Lisp and Common Lisp support the hyphen character in identifiers, Lisp code utilizes <RT-code>snake-kebab_case</RT-code>. The hyphen acts as the primary word separator. The underscore is reserved strictly as a secondary separator to group logically related portions of an identifier or to denote semantic boundaries, acting as a structural namespace within the symbol (e.g., <RT-code>city-scape_building-height</RT-code>).
+      </p>
+
+      <h2>Ad Hoc Namespaces</h2>
+      <p>
+        Emacs Lisp shares a global environment. To prevent global environment pollution, functions and global variables use the center dot (<RT-code>·</RT-code>) to separate namespace hierarchies. Spaces are omitted around the dot to ensure the symbol evaluates contiguously.
+      </p>
+      <RT-code>
+        (defconst RT·first-order-list·control·continue 0)
+      </RT-code>
+
+      <h1>Code Structure Attributes</h1>
+      <p>
+        The code structure heavily applies the RT Code Format conventions to a Lisp environment, resulting in a distinct visual footprint.
+      </p>
+
+      <h2>Global Two Space Offset</h2>
+      <p>
+        Unlike standard Emacs Lisp where top level forms like <RT-code>defun</RT-code> or <RT-code>setq</RT-code> are flush left, the entire programmatic contents of the file are indented by two spaces.
+      </p>
+
+      <h2>Function Call Enclosures</h2>
+      <p>
+        Lisp function calls are exempt from the multi-level enclosure padding rule. The outer parentheses forming a function call receive no padding, even when they contain nested data lists.
+      </p>
+      <RT-code>
+        (cat 'a 'b '( 2 (4 5)))
+      </RT-code>
+
+      <h2>Vertical Branching</h2>
+      <p>
+        The <RT-code>if</RT-code> and <RT-code>while</RT-code> statements are expanded vertically. The operator, the condition, the true branch, and the false branch each occupy their own isolated lines. 
+      </p>
+
+      <h2>Isolated Binding Blocks</h2>
+      <p>
+        In <RT-code>let</RT-code> and <RT-code>let*</RT-code> forms, the variable declaration list is pushed to its own level. The opening parenthesis sits alone on a new line. Each binding sits on its own line. The closing parenthesis sits alone, vertically aligned with the opening parenthesis. The body of the form follows on the subsequent line.
+      </p>
+
+      <h2>Using let to destructure a list</h2>
+      <p>
+        Don't destructure a list and then use the parts in a single `let*`, instead destructure the list in a first list, then use the parts in a second nested `let`.
+      </p>
+
+      <h2>Cascading Closures</h2>
+      <p>
+        When the items of a list appear one per line, i.e. for a vertical list, the closing parenthesis, and all cascading closures, appear on a line of their own at the same indention level as though an element of the list. In a sense, the innermost vertical list wins, as it will close all the vertical lists it is nested in.
+      </p>
+      <RT-code>
+        (let
+          (
+            (example_list
+              (list
+                '(4 [0])
+                '(5 [1])
+                '(6 [])
+                )))
+          ...
+      </RT-code>
+
+
+      <h2>A comment subsection</h2>
+      <p>
+        When a comment under a triple semicolon section forms a subsection, the
+        comment starts at the indention level of the code, has two semicolons.  After the comment there is a line at the indent level with only two semicolons. Then there is a blank line. The subsection contents follow.
+      </p>
+      <RT-code>
+      ;; Color logic
+      ;;
+
+      (defun RT-literal·highlight-none ()
+        nil
+        )
+
+      (defun RT-literal·highlight-default ()
+        'region
+        )
+      </RT-code>
+
+      <h2>one line specific comment</h2>
+      <p>
+        A single semicolon occurs after the code on the line, followed by a comment.
+      </p>
+      <RT-code>
+
+      </RT-code>
+
+
+      <h2>Contiguous Form Cluster</h2>
+      <p>
+        When the comment adds insight into the code, the comment typically appears with two semicolons at the same indentation level above said code. There is no following blank line.  All code the comment applies to then follows without intervening blank lines.
+      </p>
+      <RT-code>
+      ;; By contract: only called when overlay is in quoted form, and null string case already handled
+      ;; Everything from content_leftmost to content_rightmost is data, inclusive.
+      (setq RT-literal·describe-new-form_status (list 'new-good))
+      (defun RT-literal·describe-new-form_message (status)
+        nil
+        )
+      (defun RT-literal·describe-new-form (overlay_leftmost content_rightmost_right-neighbor)
+        (let*
+          ( ... )
+          ...
+          )))
+      </RT-code>
+
+      <h1>API Design Conventions</h1>
+      
+      <h2>Parameter Ordering</h2>
+      <p>
+        When defining function signatures, static configuration parameters (such as sizes, limits, or modes) must precede data payloads or instances that are actively manipulated. This facilitates partial application and logical readability.
+      </p>
+
+      <h1>TTCA Theory Terminology</h1>
+      <p>
+        The following definitions bridge the gap between abstract TTCA theory and practical implementation conventions.
+      </p>
+
+      <ul>
+        <li><RT-term>Tape</RT-term>: An array of cells. A tape has a leftmost cell and a rightmost cell.</li>
+        <li><RT-term>Rightmost Right-Neighbor</RT-term>: The cell located one position to the right of the rightmost cell. A tape area is not guaranteed to have a rightmost_right-neighbor, but it will always have a rightmost cell.</li>
+        <li><RT-term>Extent</RT-term>: The index of the rightmost cell. The index of the leftmost cell is conventionally 0. The extent defines the required bit size of an index register. When speaking of arrays of bytes, extent is the maximum byte index.</li>
+        <li><RT-term>Length</RT-term>: A count of cells in an array. Length is 1 plus extent, and can overflow an index register.</li>
+        <li><RT-term>Size</RT-term>: A count of the underlying bytes, unless otherwise noted.</li>
+        <li><RT-term>Position</RT-term>: An index (similar to a cursor position) that indicates a cell. Emacs positions differ mathematically from TTCA theory indexes.</li>
+      </ul>
+
+      <p>
+        The TTCA ontology is a topological description. It speaks of left, and right, of leftmost and rightmost.  Time dependent terms such as <RT-term>first</RT-term>  or <RT-term>last</RT-term> imply scanning/traversal.  The first cell scanned could be anywhere on the tape, depending on the algorithm used for the scan or traversal. Careful, the term <RT-term>rightmost</RT-term> refers to a cell that is included on the tape. It is an inclusive bound. Today exclusive bounds are more common. Inclusive found loops often exit from the middle.
+      </p>
+
+
+      <h2>The First/Rest Loop Technique</h2>
+
+      <p>The first/rest loop is an iterative control flow pattern designed for sequence traversal and state machine evaluation. The pattern explicitly separates the evaluation of the initial state (the "first") from the continuous cycle of advancing and re-evaluating (the "rest").</p>
+
+      <p>This technique eliminates the need for mid-loop escape clauses, such as break or throw/catch, and avoids evaluating out-of-bounds memory by ensuring the tape head is always validated before stepping.</p>
+
+      <h3>The Structural Form</h3>
+
+      <p>The structure requires performing the initial work on the first valid cell and evaluating the boundary condition simultaneously. Because a freshly cued tape machine always rests on valid data, the first cell is processed immediately within the initial variable bindings. The loop condition then evaluates both the result of that work and whether the machine has a right neighbor. Inside the loop, the machine steps, and both the work and boundary conditions are explicitly re-evaluated and updated at the absolute bottom.</p>
+
+      <p>If the work performed on the cell is extensive, a programmer should extract that logic into a helper function. This helper function is called once in the initial bindings for the "first" phase, and once inside the loop for the "rest" phase.</p>
+
+      <p>Here is the canonical RT code format for the first/rest loop:</p>
+
+      <RT-code>
+      (let
+        (
+          ;; 1. The "First" Work & Boundary Evaluation
+          (work-successful (evaluate-current-cell machine))
+          (has_right-neighbor (RT·TM·has-right-neighbor machine))
+          )
+        ;; 2. The rest loop
+        (while (and work-successful has_right-neighbor)
+          (progn
+            ;; 3. The "Step" phase
+            (RT·TM·step machine)
+
+            ;; 4. The "Rest" Work & Boundary Evaluation
+            (setq work-successful (evaluate-current-cell machine))
+            (setq has_right-neighbor (RT·TM·has-right-neighbor machine))
+            ))
+        ;; Return the final state of the work
+        work-successful
+        )
+      </RT-code>
+
+      <p>
+        Elisp does not have a middle-of-loop exit, but if it did, the logic would look like this:
+      </p>
+
+      <RT-code>
+      (let
+        (        
+          (work-successful nil)
+          (has_right-neighbor nil)
+          )
+        (loop
+          (setq work-successful (evaluate-current-cell machine))
+          (setq has_right-neighbor (RT·TM·has-right-neighbor machine))
+
+          (if
+            (not (and work-successful has_right-neighbor)) 
+            (break)
+            (RT·TM·step machine)
+            ))
+        work-successful
+        )
+      </RT-code>
+
+      <p>Which eliminates the need for two calls to <RT-code>evaluate-current-cell</RT-code>  and <RT-code>has-right-neighbor</RT-code>. This can be done in other languages that have a loop <RT-code>break</RT-code>. (Donald Knuth argued in his 1974 paper, Structured Programming with go to Statements, that forcing programmers to duplicate code simply to satisfy a rigid while or repeat/until pre/post-test constraint was a failure of the language's expressiveness. Also discussed in the Art of Computer Programming.)
+      </p>
+
+      <p>If we are willing to us catch and throw in normal control flow, this can be done in elisp as:
+      </p>
+
+      <RT-code>
+      (catch 'loop-exit
+        (while t
+          (let
+            (
+              (work-successful (evaluate-current-cell machine))
+              (has_right-neighbor (RT·TM·has-right-neighbor machine))
+              )
+            (if
+              (not (and work-successful has_right-neighbor)) 
+              (throw 'loop-exit work-successful)
+              (RT·TM·step machine)
+              ))))
+      </RT-code>
+
+      <p>So defining:</p>
+      <RT-code>
+      (defmacro RT·loop (&rest body)
+        "An infinite loop construct designed to be exited via RT·break."
+        `(catch 'loop-exit
+           (while t
+             ,@body
+             )))
+
+      (defmacro RT·break (&optional return_val)
+        "Break out of an RT·loop, optionally returning RETURN_VAL."
+        `(throw 'loop-exit ,return_val)
+        )
+      </RT-code>
+
+      <p>Our example becomes:</p>
+      
+      <RT-code>
+      (let
+        (        
+          (work-successful nil)
+          (has_right-neighbor nil)
+          )
+        (RT·loop
+          ;; 1. The Work & Boundary Evaluation
+          (setq work-successful (evaluate-current-cell machine))
+          (setq has_right-neighbor (RT·TM·has-right-neighbor machine))
+
+          ;; 2. The Mid-Loop Condition Check
+          (if
+            (not (and work-successful has_right-neighbor)) 
+            (RT·break work-successful)
+            ;; 3. The "Step" phase
+            (RT·TM·step machine)
+            ))
+        )
+      </RT-code>
+
+      <p>
+        Production code first/rest loop example without a middle loop break.
+      </p>
+
+      <RT-code>
+      (defun RT·TM·sequence·eq (TM_substrate TM_target)
+        (let
+          (
+            (TM-substrate-copy (RT·TM·entangled-copy TM_substrate))
+            (TM-target-copy (RT·TM·entangled-copy TM_target))
+            )
+          (let
+            (
+              (are-eq (= (RT·TM·read TM-substrate-copy) (RT·TM·read TM-target-copy)))
+              (target-has-right-neighbor (RT·TM·has-right-neighbor TM-target-copy))
+              (substrate-has-right-neighbor (RT·TM·has-right-neighbor TM-substrate-copy))
+              )
+            (while 
+              (and are-eq target-has-right-neighbor substrate-has-right-neighbor)
+              (progn
+                (RT·TM·step TM-substrate-copy) 
+                (RT·TM·step TM-target-copy)
+                (setq are-eq (= (RT·TM·read TM-substrate-copy) (RT·TM·read TM-target-copy)))
+                (setq target-has-right-neighbor (RT·TM·has-right-neighbor TM-target-copy))
+                (setq substrate-has-right-neighbor (RT·TM·has-right-neighbor TM-substrate-copy))
+                ))
+            (and are-eq (not target-has-right-neighbor))
+            )))
+      </RT-code>
+
+      <p> If the double call code is substantial, and we do not want to use a middle break, it can be put in a helper function, and then this reduces to two calls to the helper. the helper function is called in the first part and the rest loop. If the programmer tries to trick the loop by giving the variables fake values in the first part, the first/rest pattern will be broken.
+      </p>
+
+      <h3>Relationship between first/rest and Tail Recursion</h3>
+
+      <p>The first/rest loop is the exact iterative equivalent of a tail-recursive function.</p>
+
+      <p>In a functional paradigm, a sequence is algebraically defined as a head (first) and a tail (rest). A tail-recursive function evaluates the head, performs the necessary work, and then returns the result of calling itself on the tail.</p>
+
+      <p>Environments lacking Tail Call Optimization (TCO), such as Emacs Lisp, will throw a stack overflow error if a recursive function traverses a long sequence. The first/rest loop flattens this recursive mathematical model into memory-safe iteration by mapping the recursive phases directly to iterative steps:</p>
+
+      <p>The Initial Function Call: The initial work and the let block bindings in the first/rest loop mirror the initial execution and argument evaluation of the recursive function.</p>
+
+      <p>The Base Case Check: The while loop condition mirrors the recursive base case. If the condition fails, the loop terminates, mimicking the recursive function returning its final value.</p>
+
+      <p>The Tail Call: The step and setq updates at the bottom of the loop body mirror the argument passing of the tail-recursive call. Updating the variables and allowing the while loop to jump back to the top is functionally identical to the function invoking itself with a new set of parameters for the "rest" of the sequence.</p>
+
+      <p>By adhering to this pattern, a programmer retains the strict, predictable state management of functional recursion while satisfying the physical memory constraints of an iterative runtime environment.</p>
+
+      <p>To illustrate the mathematical equivalence, here is the exact same logic written as a tail-recursive function. Note that while this form is theoretically pure, the first/rest iterative loop is strictly preferred in Emacs Lisp. Because Elisp lacks Tail Call Optimization (TCO), this recursive form would eventually exhaust the call stack and crash when traversing massive sequences.</p>
+
+      <RT-code>
+      (defun process-sequence-recursively (machine)
+        (let
+          (
+            ;; 1. The "First" Work & Boundary Evaluation
+            (work-successful (evaluate-current-cell machine))
+            (active (RT·TM·has-right-neighbor machine))
+            )
+          ;; 2. The Condition check
+          (if (and work-successful active)
+            (progn
+              ;; 3. The "Step" phase
+              (RT·TM·step machine)
+
+              ;; 4. The Tail Call (The "Rest")
+              ;; This directly replaces the setq updates and loop jump
+              (process-sequence-recursively machine)
+              )
+            ;; Base case: loop terminates, return final state
+            work-successful
+            )))
+      </RT-code>
+
+      <h2>Exercise</h2>
+      <p>Show the 'production code' with a first/rest pattern using the RT macros and a middle break loop.</p>
+
+    </RT-article>
+  </body>
+</html>
diff --git a/developer/document/RT-code-format.html b/developer/document/RT-code-format.html
new file mode 100644 (file)
index 0000000..8260a9f
--- /dev/null
@@ -0,0 +1,281 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>RT code format conventions</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="Thomas Walker Lynch" 
+        date="2026-03-11" 
+        title="RT code format conventions">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <p>
+        This document has been evolving. Consequently there is a body of non-conforming code.  Whenever we run up against it, it is nice to update it.
+      </p>
+
+      <h1>Object vs. instance nomenclature</h1>
+      <p>
+        It is too much of an ask to remove the word 'object' from the English language, and then set it aside and give it a esoteric technical meaning. This is why the term is often misused. So we instead talk about an <RT-term>interface</RT-term> as being a set of functions that share one or more state variables. The term <RT-term>instance</RT-term> is then a collection of such state variables and their values sitting in memory. Interface functions are usually grouped within a named container, and state variables are often grouped into a single named data structure.
+      </p>
+      <p>
+        In some languages the instance occurs to the left of a lower dot (period) operator and is referred to as <RT-code>this</RT-code> from inside the interface functions.  In other languages there is a convention where the instance is passed in as the first parameter to interface functions.  There can also be interface functions that accept multiple instances of the same type. Bridge interfaces can be created that accept two instances potentially of different types.
+      </p>
+      <p>
+        Since we have released <RT-term>object</RT-term> from captivity,  a programmer can talk about math objects, as things found in mathematics, and C objects, as things found in the C language, even though they are not instantiated from classes, or might not even be data. For example, the <RT-term>for loop</RT-term> is a C object.
+      </p>
+
+      <h1>Identifier names</h1>
+
+      <h2>Case</h2>
+      <ul>
+        <li>Types, modules: <RT-code>PascalCase</RT-code></li>
+        <li>Functions, variables: <RT-code>snake-kebab_case</RT-code>. If a language does not allow the <RT-code>-</RT-code> character in identifiers, then <RT-code>snake-kebab_case</RT-code> reduces to RT-code>snake_case</RT-code>.</li>
+        <li>Globals: <RT-code>UPPER_SNAKE-KEBAB_CASE</RT-code>, that, reduces to <RT-code>UPPER_SNAKE_CASE</RT-code> when the language does not allow the <RT-code>-</RT-code> character in identifiers.</li>
+      </ul>
+
+      <h2>Proper nouns and acronyms</h2>
+      <p>
+        Even in <RT-code>PascalCase</RT-code> and <RT-code>snake-kebab_case</RT-code>, proper nouns and acronyms remain capitalized, as per standard English language conventions (e.g., <RT-code>IEEE_publication-count</RT-code>).
+      </p>
+
+      <h2>Abbreviations</h2>
+      <p>
+        For outer scope identifiers we spell things out for clarity. This follows Lisp programming culture. This makes it clear for people who are 'not in the club' to be able to get started and read the code, and tends to be self documenting. Our file system names mirror our program identifier rules, so you find things such as 'library', 'source', spelled out.
+      </p>
+      <p>
+        For long words, inner scope identifiers, temporary variables, and conventional suffixes, abbreviations become more common. By the time we get to inner loops variable names, such as loop counters are typically 1 letter long. 
+      </p>
+
+      <h2>Primary and secondary separator use</h2>
+      <p>
+        When a language supports the dash, <RT-code>-</RT-code>, dash connected components are given precedence and of choice, and bind semantically higher than under score separated components. E.g. <RT-code>rounded_x-coordinate</RT-code>. 
+      </p>
+      <p>
+        Otherwise, if the language does not support hyphens in identifiers (such as C, Python, and Java), we conventionally drop the nicety of multiple semantic bindings, e.g. <RT-code>rounded_x_coordinate</RT-code>. However if the distinction has high value, a double underscore and be used, <RT-code>rounded__x_coordinate</RT-code>. 
+      </p>
+
+      <h2>Ad hoc namespacing</h2>
+      <p>
+      Most parsers now a allow a center dot (<RT-code>·</RT-code>) in identifiers. In languages were namespaces are not explicitly available, we use the cdot to represent a namespace. Namespaces are closely related to module names, class names, interface names, and to type names,so all are written in PascalCase.  So for example, <RT-code>Math·rounded__x_coordinate</RT-code>, might be variable in the <RT-code>Math</RT-code> namespace, a function on the <RT-code>Math</RT-code> interface, etc.
+
+      <h2>Component order</h2>
+      <p>
+        As long as it is sensible, and doesn't break parallel constructions, place the least changing component, or the broadest category, to the left of the identifer. For example, a conditional write functions would be called,  `write-conditional`,  not `conditional-write`  because it is a refinement on the general category or write functions.
+      </p>
+
+      <h2>Copy,read, write</h2>
+      <p>
+        'read' and 'write', are the two ends of a copy.  When 'this instance' is understood as being one end of the copy, then using 'read' or 'write' makes sense. However, it is generally  better to provide an external copy command. The order of arguments in a copy command are  `read from source value -->  write to destination value` i.e. data flows from left to write.  (This is contrary to an assignment operator, which has data flowing from right to left during a copy.)
+      </p>
+
+      <h2>The term <RT-term>make</RT-term></h2>
+      <p>
+        When allowed by the langauge, factory functions are called 'make'.  They are not called 'create', nor 'new'. 
+      </p>
+
+      <h2>Function terminology</h2>
+      <p>
+        Parameters are what give a function its characteristics.  They are typically static.  They are one step away from values that are curried into a function. Parameter variables are given with the function definition, and parameter values are given to the function at run time when it is called.
+      </p>
+      <p>
+        Given argument values can be said to be bound to arguments variables upon call.  
+      </p>
+      <p>
+        Arguments drive the function computation. Conceptually they might change on every separate call, so they are highly dynamic. Argument variables are specified when the function is defined. Argument values are given to the function at call time.
+      </p>
+      <p>
+        Values are said to be <RT-term>given</RT-term> to a function. We don't say a function  <RT-term>takes</RT-term> arguments, as though a call stack could reach into memory and grab values.  We use terms <RT-term>accept</RT-term> and <RT-term>reject</RT-term> of values for guard code. For a function to <RT-term>accept</RT-term> an argument value is to mean that argument value was tested and passed.  As an example, we can say that a square root function only accepts values greater than or equal to zero, or that it <RT-term>rejects</RT-term> negative values.
+      </p>
+      <p>
+        <RT-term>Operators</RT-term> are logically functions by another name. Syntactically infix notation is often used. <RT-term>Operands</RT-term> are arguments given to an operator.
+      </p>
+
+      <h2>Plural identifiers</h2>
+
+      <p>
+        Do not make the names of containers plural, instead prefix the container abstract type, or actual type before the identifier. Recall that types are PascalCase.
+      </p>
+
+      <ul>
+        <li><RT-code>list_*</RT-code> / <RT-code>seq_*</RT-code> : generic ordered items / indexed items</li>
+        <li><RT-code>Map_*</RT-code> / <RT-code>dict_*</RT-code> : keyed containers</li>
+      </ul>
+
+      <p>Add a type prefix when it adds clarity.</p>
+      <ul>
+        <li><RT-code>count-of_*</RT-code> : number of elements</li>
+        <li><RT-code>Bool_*</RT-code> : <RT-code>Bool</RT-code> type</li>
+        <li><RT-code>flag_*</RT-code> : value used as a Boolean flag</li>
+      </ul>
+
+      <h2>Identifiers for directory/file names</h2>
+      <ul>
+        <li><RT-code>dir_*</RT-code> : directory name</li>
+        <li><RT-code>dirn_*</RT-code> : directory name</li>
+        <li><RT-code>dirp_*</RT-code> : directory path</li>
+        <li><RT-code>dirpr_*</RT-code> / <RT-code>dirpa_*</RT-code> : relative / absolute directory path</li>
+        <li><RT-code>file_*</RT-code> : file name</li>
+        <li><RT-code>filen_*</RT-code> : file name</li>
+        <li><RT-code>filep_*</RT-code> : file path</li>
+        <li><RT-code>filepr_*</RT-code> / <RT-code>fpr_*</RT-code> : relative path</li>
+        <li><RT-code>filepa_*</RT-code> / <RT-code>fpa_*</RT-code> : absolute path</li>
+        <li><RT-code>fsnodn_*</RT-code> : file system node name
+        <li><RT-code>fsnodp_*</RT-code> : file system node path 
+        <li><RT-code>fsnodpr_*</RT-code> / <RT-code>fsnpr_*</RT-code> : relative</li>
+        <li><RT-code>fsnodpa_*</RT-code> / <RT-code>fsnpa_*</RT-code>: absolute</li>
+      </ul>
+
+      <h1>Comma separated lists</h1>
+
+      <h2>Horizontal comma list</h2>
+      <p>The comma is preceded by a space and abuts the item it follows.</p>
+      <RT-code>
+        int x ,y ,z;
+      </RT-code>
+
+      <h2>Vertical comma list</h2>
+      <p>The comma is placed <em>before</em> the item on the new line, aligned with the item's indentation. This applies to all languages, including Python and C.</p>
+      <RT-code>
+        result = some_function(
+          first_argument
+          ,second_argument
+          ,third_argument
+        );
+      </RT-code>
+
+      <h1>Enclosure spacing</h1>
+
+      <h2>Single-level enclosures</h2>
+      <p>No space padding inside the enclosure punctuation.</p>
+      <RT-code>
+        if(condition){
+          do_something();
+        }
+      </RT-code>
+
+      <h2>Multi-level enclosures</h2>
+      <p>One space of padding is applied <em>only</em> to the outermost enclosure punctuation.</p> 
+
+      <RT-code>
+        if( f(g(x)) ){
+          do_something();
+        }
+      </RT-code>
+
+      <p>This rule is not applied to function calls in Lisp. For example, the outer parentheses forming a function call receive no padding, even when they contain nested data lists:</p>
+      
+      <RT-code>
+        (cat 'a 'b '( 2 (4 5)))
+      </RT-code>
+
+      <h1>Indentation</h1>
+      <ul>
+        <li>Strictly two spaces per indentation level.</li>
+        <li>Never use tabs.</li>
+        <li>Nest lines under the syntactic element that opened them.</li>
+      </ul>
+      <p>Python enforces indentation syntactically. Use two-space indentation for all Python code, even though four is common in the wider ecosystem.</p>
+
+      <h1>The CLI vs. work function pattern</h1>
+      <p>
+        To avoid the "String Trap" (where logic is tightly coupled to the terminal and requires string serialization to reuse) executable modules must separate the Command Line Interface from the core logic.
+      </p>
+      <ul>
+        <li><strong>Work Functions:</strong> Implement core logic. They accept and return native instances (ints, lists, paths), never look at <RT-code>sys.argv</RT-code> (or equivalent), and do not depend on being run from a command line.</li>
+        <li><strong>CLI Function:</strong> The bridge between the OS and the Work Function. It is always named <RT-code>CLI()</RT-code>. It parses string arguments into native types, calls the Work Function, formats the output for the user, and translates exceptions into exit codes.</li>
+      </ul>
+
+      <h2>Python application</h2>
+      <p>Put argument parsing and <RT-code>if __name__ == "__main__":</RT-code> in the CLI section. Keep side effects out of import time.</p>
+
+      <h2>Bash application</h2>
+      <p>Bash scripts should start with <RT-code>#!/usr/bin/env bash</RT-code> and <RT-code>set -euo pipefail</RT-code>. RT-style Bash separates a small top-level CLI harness from a set of functions that implement the work. Parse arguments into variables, call a main function with explicit parameters, and avoid relying on global mutable state where possible.</p>
+
+      <h1>C addendum: error handling and ownership</h1>
+      <ul>
+        <li>Functions should document ownership of pointers and lifetimes.</li>
+        <li>Prefer explicit <RT-code>*_count</RT-code> parameters over sentinel values when passing arrays.</li>
+      </ul>
+
+      <h1>Automated formatting tools</h1>
+      <p>
+        To ensure consistency without manual drudgery, the Harmony skeleton provides a dedicated code formatter called <RT-code>RTfmt</RT-code>. A person can find this tool in the <RT-code>shared/tool/</RT-code> directory.
+      </p>
+
+      <h2>The RTfmt CLI</h2>
+      <p>
+        <RT-code>RTfmt</RT-code> is a shallow-tokenizing formatter written in Python. It parses source code into structural blocks (strings, comments, commas, and enclosures) without needing a full Abstract Syntax Tree. This architecture allows it to safely enforce RT formatting rules across multiple languages while preserving indentation and protecting native operators.
+      </p>
+      <ul>
+        <li><RT-code>RTfmt write &lt;file...&gt;</RT-code> : Formats files in place. It performs a content comparison before writing, leaving the file modification timestamp untouched if the file is already compliant.</li>
+        <li><RT-code>RT-formatter pipe</RT-code> : Reads from standard input and writes to standard output, making it ideal for editor integration.</li>
+        <li><RT-code>--lisp</RT-code> : A flag that instructs the formatter to skip the outermost enclosure padding rule, honoring the Lisp function call exception.</li>
+      </ul>
+
+      <h2>Emacs integration</h2>
+      <p>
+        For Emacs users, the <RT-code>RT-formatter.el</RT-code> file provides the <RT-code>RT-formatter-buffer</RT-code> interactive command.
+      </p>
+      <p>
+        This wrapper passes the current buffer through the <RT-code>RT-formatter pipe</RT-code> command. It automatically detects Lisp-derived modes to append the <RT-code>--lisp</RT-code> flag. Furthermore, it utilizes a non-destructive buffer replacement strategy. This ensures that a programmer's cursor position, selection marks, and window scroll state remain anchored exactly where they were before the formatting occurred.
+      </p>
+
+      <h1>Exercises</h1>
+      <h2>Exercise 1: Comma and function call formatting</h2>
+      <p>Reformat the following C code snippet to strictly adhere to the RT code format rules.</p>
+      <RT-code>
+        void my_function(int a, int b, int c) {
+          int result = calculate_value(a, b, c);
+          printf("Result: %d, a: %d, b: %d, c: %d\n", result, a, b, c);
+        }
+      </RT-code>
+
+      <h2>Exercise 2: Multi-level enclosure and short stuff rule</h2>
+      <p>Reformat the following C code snippet to use the multi-level enclosure rule and the short stuff rule.</p>
+      <RT-code>
+        if (check_permissions(user_id, file_path) && is_valid(file_path)) {
+          for (int i = 0; i < 10; i++) {
+            if (i % 2 == 0) {
+              printf("Even: %d\n", i);
+            }
+          }
+        }
+      </RT-code>
+
+      <h2>Exercise 3: Identifier Naming Conventions</h2>
+      <p>
+        Rename the following poorly named variables to strictly adhere to the RT code format rules. Assume these are variables in a C or Python program (using standard <RT-code>snake_case</RT-code>). Pay close attention to proper nouns, acronyms, and the expanded suffix semantics.
+      </p>
+      <ul>
+        <li>A variable holding a list of addresses for HTTP servers: <RT-code>httpServers</RT-code></li>
+        <li>A variable counting the total number of parsed HTML nodes: <RT-code>num_html_nodes</RT-code></li>
+        <li>A custom type representing a JSON payload: <RT-code>json_payload</RT-code></li>
+        <li>A boolean indicating if the NASA data download is complete: <RT-code>isNasaDone</RT-code></li>
+        <li>A variable holding multiple absolute file paths for images: <RT-code>imageFiles</RT-code></li>
+      </ul>
+
+      <h2>Exercise 4: Identifier naming with hyphens</h2>
+      <p>
+        Rename the same poorly named variables from Exercise 3, but this time assume they are written in a language that supports the hyphen (<RT-code>-</RT-code>) in identifiers. Apply the rules for primary and secondary separators, keeping in mind the structural boundary of suffixes.
+      </p>
+      <ul>
+        <li>A variable holding a list of addresses for HTTP servers: <RT-code>httpServers</RT-code></li>
+        <li>A variable counting the total number of parsed HTML nodes: <RT-code>num_html_nodes</RT-code></li>
+        <li>A custom type representing a JSON payload: <RT-code>json_payload</RT-code></li>
+        <li>A boolean indicating if the NASA data download is complete: <RT-code>isNasaDone</RT-code></li>
+        <li>A variable holding multiple absolute file paths for images: <RT-code>imageFiles</RT-code></li>
+      </ul>
+
+    </RT-article>
+  </body>
+</html>
diff --git a/developer/document/naming_file-and-directory.html b/developer/document/naming_file-and-directory.html
new file mode 100644 (file)
index 0000000..c4bd65c
--- /dev/null
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>File and directory naming conventions</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="RT" 
+        date="2026-03-09" 
+        title="File and directory naming conventions">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <h1>Program file system objects</h1>
+
+      <p>
+        Harmony files and directories are considered to be program file systems objects.
+      </p>
+
+      <h2>Files</h2>
+      <p>
+        Files that hold a single class, have he same base name as that class. If a file holds multiple classes, but there is a class of primary importance and auxiliary classes, the file carries the name of the primary class.
+      </p>
+
+      <p>
+        If a file represents a module or name space, follow the same naming conventions specified for classes and name spaces in the <RT-code>format_RT-code.html</RT-code> document.
+      </p>
+
+      <p>
+        Other program files follow the same naming conventions specified in the <RT-code>format_RT-code.html</RT-code> document for an identifier of the
+        contained or related language.
+      </p>
+
+      <h2>Hyphens and spaces</h2>
+
+      <p>In a C language culture, file names use underscores between words. In a Lisp language culture use snake-kebab_case. Spaces are generally not used in program file names. Though the Harmony skeleton is language agnostic, it was first used with C, hence file names use primarily underscores.
+      </p>
+
+
+      <h1>Program Directories</h1>
+      <p>
+        A directory that is a package, follows the convention for name spaces.
+      </p>
+
+      <p>
+        A directory that is used to give the contained files a property is an identifier.
+      </p>
+      
+      <p>
+        What is a reasonable primary property for a set of files? Perhaps:
+      </p>
+      <ul>
+        <li>Who uses each file with this property. Home directories are named like this.</li>
+        <li>The role of the people using the file. The <RT-code>developer</RT-code> and <RT-code>tester</RT-code> directories were named in this manner.</li>
+        <li>What program are the files for.</li>
+        <li>The generic category of program said files are for. We prefer the names <RT-code>authored</RT-code> and <RT-code>made</RT-code>. <RT-code>authored</RT-code> files are those written by humans or AI, while <RT-code>made</RT-code> files are products of tools.</li>
+      </ul>
+
+      <h1>Property indicators on files</h1>
+      <p>
+        We add a second property to a file using dot extensions to the file's name. We can extend the dot suffix model by using multiple dot suffixes. File name extensions are used to signal to the build tools how the file is to be processed:
+      </p>
+      <ul>
+        <li><RT-code>.cli.c</RT-code> : Compiler fodder made into a stand-alone executable.</li>
+        <li><RT-code>.lib.c</RT-code> : Library code source, compiled as an object file and added to the project archive.</li>
+        <li><RT-code>.mod.c</RT-code> : Kernel module sources.</li>
+      </ul>
+
+      <h1>Exercises</h1>
+      <h2>Exercise 1: Program file names</h2>
+      <p>
+        Rename the following poorly named files to strictly adhere to the RT naming conventions. Pay close attention to the language culture, the capitalization of acronyms, and the rule for spelling out abbreviations in outer scope identifiers.
+      </p>
+      <ul>
+        <li>A C-culture makefile that builds a target library and command line interface: <RT-code>target-lib-cli.mk</RT-code></li>
+        <li>A Lisp-culture source file for an HTML parsing module: <RT-code>html_parser_mod.el</RT-code></li>
+        <li>A C language source file that initializes an HTTP server: <RT-code>init-http-srv.c</RT-code></li>
+      </ul>
+
+    </RT-article>
+  </body>
+</html>
diff --git a/developer/document/single-file_C-module-and-namespace.html b/developer/document/single-file_C-module-and-namespace.html
new file mode 100644 (file)
index 0000000..23af5f8
--- /dev/null
@@ -0,0 +1,171 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>C modules, namespaces, and the build lifecycle</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="Thomas Walker Lynch" 
+        date="2026-03-11" 
+        title="C modules, namespaces, and the build lifecycle">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <h1>Single-file C source</h1>
+      <p>
+        The RT C language culture abandons the traditional separation of declarations into <RT-code>.h</RT-code> header files and definitions into <RT-code>.c</RT-code> source files. A developer integrates the interface and the implementation into a single file. 
+      </p>
+      <p>
+        When a person includes the file using an <RT-code>#include</RT-code> directive, the file exposes only its interface. When the build system compiles the file into an object, a preprocessor macro acts as an implementation gate to compile the definitions. This keeps the source tree clean and ensures the interface and implementation never fall out of synchronization.
+      </p>
+
+      <h1>Ad hoc namespaces</h1>
+      <p>
+        The C language lacks native namespaces. To prevent symbol collisions in large projects, RT code format uses a center dot (<RT-code>·</RT-code>) to denote ad hoc namespaces within identifiers. 
+      </p>
+      <p>
+        A programmer maps the directory structure directly to these namespaces. For example, a file located at <RT-code>authored/ExampleGreet/Math.lib.c</RT-code> belongs to the <RT-code>ExampleGreet</RT-code> namespace and defines the <RT-code>Math</RT-code> module. 
+      </p>
+      <ul>
+        <li>Types and modules use <RT-code>PascalCase</RT-code>.</li>
+        <li>Functions and variables use <RT-code>snake_case</RT-code>.</li>
+      </ul>
+      <p>
+        An exported function from this module carries the full namespace and module prefix, such as <RT-code>ExampleGreet·Math·add</RT-code>.
+      </p>
+
+      <h1>The implementation gate</h1>
+      <p>
+        To achieve the single-file source pattern, the code relies on two preprocessor constructs:
+      </p>
+      <ul>
+        <li><strong>Include Guard:</strong> The entire file is wrapped in an include guard using the <RT-code>·ONCE</RT-code> suffix. This prevents redeclaration errors if the file is included multiple times.</li>
+        <li><strong>Implementation Block:</strong> The definitions are wrapped in a single <RT-code>#ifdef</RT-code> block using the exact namespace and module name. The build system dynamically injects this macro via a <RT-code>-D</RT-code> compiler flag when building the module's specific object file.</li>
+      </ul>
+      
+      <h2>Build system mechanics</h2>
+      <p>
+        When a consumer file, such as <RT-code>hello.CLI.c</RT-code>, contains <RT-code>#include "Math.lib.c"</RT-code>, the compiler processes the file without the <RT-code>ExampleGreet·Math</RT-code> macro defined. It skips the implementation and reads only the function prototype.
+      </p>
+      <p>
+        When the orchestrator compiles the library object, it evaluates the target name and explicitly passes <RT-code>-DExampleGreet·Math</RT-code> to the compiler. This unlocks the gate, compiling the machine code for the definitions into <RT-code>Math.lib.o</RT-code>.
+      </p>
+
+      <h2>Example: Math.lib.c</h2>
+      <p>
+        The following example demonstrates the complete structure.
+      </p>
+
+      <RT-code>
+        #ifndef ExampleGreet·Math·ONCE
+        #define ExampleGreet·Math·ONCE
+
+        int ExampleGreet·Math·add(int a ,int b);
+
+        #ifdef ExampleGreet·Math
+
+        int ExampleGreet·Math·add(int a ,int b){
+          return a + b;
+        }
+
+        #endif // ExampleGreet·Math
+        #endif // ExampleGreet·Math·ONCE
+      </RT-code>
+
+      <h2>Cross-module dependencies</h2>
+      <p>
+        When one module depends on another, the developer directly includes the library source file. For example, if the Greeter module requires the Math module, the file <RT-code>Greeter.lib.c</RT-code> will contain:
+      </p>
+      <RT-code>
+        #include "Math.lib.c"
+      </RT-code>
+      <p>
+        Because every file is protected by a <RT-code>·ONCE</RT-code> include guard, it is safe for multiple modules to include the same dependency. The preprocessor will expand the interface once per translation unit.
+      </p>
+
+      <h2>Information hiding</h2>
+      <p>
+        To define internal helper functions or private data that should not be exposed in the module's public interface, a programmer places them strictly inside the <RT-code>#ifdef</RT-code> implementation block.
+      </p>
+      <p>
+        To prevent these internal symbols from leaking into the global namespace during linking, they must be given internal linkage. The RT skeleton provides the <RT-code>Local</RT-code> macro (defined as <RT-code>static</RT-code> in <RT-code>RT_global.h</RT-code>) for this exact purpose.
+      </p>
+      <RT-code>
+        #ifdef ExampleGreet·Math
+
+        Local int internal_helper(int val){
+          return val * 2;
+        }
+
+        int ExampleGreet·Math·add(int a ,int b){
+          return internal_helper(a) + b;
+        }
+
+        #endif // ExampleGreet·Math
+      </RT-code>
+
+      <h2>Executable entry points</h2>
+      <p>
+        Programs intended to be compiled into standalone executables use the <RT-code>.CLI.c</RT-code> suffix. These files consume the library modules but do not define their own namespaces or include guards, as they are never included by other files.
+      </p>
+      <p>
+        A <RT-code>.CLI.c</RT-code> file includes the necessary <RT-code>.lib.c</RT-code> files, parses command-line arguments in the <RT-code>main</RT-code> function, and passes native data types to a dedicated <RT-code>CLI()</RT-code> function to orchestrate the core logic.
+      </p>
+
+      <h1>Directory structure and namespaces</h1>
+      <p>
+        The physical layout of the <RT-code>authored</RT-code> directory dictates how the build orchestrator finds the source code.
+      </p>
+      <ul>
+        <li><strong>Flat Structure:</strong> If a project does not utilize namespaces, the <RT-code>.lib.c</RT-code> and <RT-code>.CLI.c</RT-code> files reside directly inside the <RT-code>developer/authored/</RT-code> directory.</li>
+        <li><strong>Namespaced Structure:</strong> If a project utilizes namespaces, the files are grouped into subdirectories matching the namespace name, such as <RT-code>developer/authored/ExampleGreet/</RT-code>.</li>
+      </ul>
+
+      <h1>Making the code</h1>
+      <p>
+        The developer compiles the code using the <RT-code>make</RT-code> wrapper script located in <RT-code>developer/tool/make</RT-code>. This script accepts two primary arguments: the build command and the target namespace.
+      </p>
+
+      <RT-code>
+        > make &lt;command&gt; [namespace]
+      </RT-code>
+
+      <p>
+        To build the <RT-code>ExampleGreet</RT-code> code, a person issues the following command from the developer workspace:
+      </p>
+
+      <RT-code>
+        > make all ExampleGreet
+      </RT-code>
+
+      <p>
+        Behind the scenes, the <RT-code>make</RT-code> wrapper assigns the namespace to an environment variable and calls the master orchestrator makefile. The orchestrator detects the namespace, updates the source directory search path to <RT-code>authored/ExampleGreet</RT-code>, and injects the appropriate namespace macros during compilation. 
+      </p>
+      <p>
+         The orchestrator compiles the <RT-code>.lib.c</RT-code> files into object files under <RT-code>scratchpad/build/object/</RT-code>, archives them into a library file, and links the <RT-code>.CLI.c</RT-code> objects into standalone executables. The final artifacts are placed in the <RT-code>scratchpad/made/</RT-code> directory.
+      </p>
+
+      <h1>Promoting the artifacts</h1>
+      <p>
+        Artifacts resting in the scratchpad are volatile and private to the developer. To share the executables and libraries with the rest of the project (such as the tester or consumer roles), the developer must promote them.
+      </p>
+      
+      <RT-code>
+        > promote write
+      </RT-code>
+
+      <p>
+        The <RT-code>promote</RT-code> tool compares the contents of <RT-code>developer/scratchpad/made/</RT-code> against <RT-code>consumer/made/</RT-code>. It atomically copies any new or updated files to the consumer workspace, stripping write permissions to enforce the immutability of the deployment target. Once promoted, the <RT-code>hello</RT-code> executable is ready for testing.
+      </p>
+
+    </RT-article>
+  </body>
+</html>
diff --git a/developer/tool/do-all b/developer/tool/do-all
new file mode 100755 (executable)
index 0000000..2350fd8
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+
+# input guards
+
+  setup_must_be="developer/tool/setup"
+  if [ "$SETUP" != "$setup_must_be" ]; then
+    echo "$(script_fp):: error: must be run in the $setup_must_be environment"
+    exit 1
+  fi
+
+set -e
+set -x
+
+  cd "$REPO_HOME"/developer || exit 1
+  # /bin/make -f tool/makefile $@
+
+  scratchpad clear
+  make
+  release clean
+  release write
+
+set +x
+echo "$(script_fn) done."
index 42babce..b7e181b 100755 (executable)
-#!/usr/bin/env bash
-script_afp=$(realpath "${BASH_SOURCE[0]}")
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+developer/tool/make - Build Orchestrator Wrapper
 
-# input guards
+This script acts as the primary entry point for compiling the project.
+It parses colon-separated arguments mapping specific namespace patterns
+(including file globs) to their intended build commands.
 
-  setup_must_be="developer/tool/setup"
-  if [ "$SETUP" != "$setup_must_be" ]; then
-    echo "$(script_fp):: error: must be run in the $setup_must_be environment"
-    exit 1
-  fi
+A program developer can modify this script to add custom pre-build checks,
+custom argument parsing, or project-specific routing logic.
+"""
 
-set -e
-set -x
+import sys ,os ,subprocess ,fnmatch
+from typing import List
 
-  cd "$REPO_HOME"/developer || exit 1
-  # /bin/make -f tool/makefile $@
+def check_environment() -> None:
+  setup_must_be = "developer/tool/setup"
+  current_setup = os.environ.get("SETUP" ,"")
+  if( current_setup != setup_must_be ):
+    print(f"developer/tool/make:: error: must be run in the {setup_must_be} environment" ,file=sys.stderr)
+    sys.exit(1)
 
-  mkdir -p scratchpad/stage && cp -au authored/RT scratchpad/stage/
+def get_namespaces() -> List[str]:
+  authored_dir = os.path.join(os.environ.get("REPO_HOME" ,".") ,"developer" ,"authored")
+  if( not os.path.isdir(authored_dir) ):
+    return []
+  
+  namespaces = []
+  for TM_name in os.listdir(authored_dir):
+    path = os.path.join(authored_dir ,TM_name)
+    if( os.path.isdir(path) and not TM_name.startswith(".") ):
+      namespaces.append(TM_name)
+  namespaces.sort()
+  return namespaces
 
-set +x
-echo "$(script_fn) done."
+def print_usage() -> None:
+  print("Usage: make [<namespace_pattern>[:<command>]*]*\n")
+  print("Commands:")
+  print("  all          Build library, CLI, and kmod (Default)")
+  print("  library      Build only the library archives")
+  print("  CLI          Build only the CLI executables")
+  print("  kmod         Build kernel modules")
+  print("  clean        Remove build artifacts")
+  print("  information  Display build environment variables\n")
+  
+  print("Available namespaces:")
+  namespaces = get_namespaces()
+  if( namespaces ):
+    for TM_ns in namespaces:
+      print(f"  {TM_ns}")
+  else:
+    print("  (No namespaces currently found in authored/)")
+  
+  print("\nExamples:")
+  print("  make                                      (Displays this usage message)")
+  print("  make ExampleGreet                         (Builds 'all' for ExampleGreet)")
+  print("  make Ex*:library G*:all:information       (Builds 'library' for Ex*, 'all' and 'information' for G*)")
+  print("  make \"*:clean\"                            (Cleans all namespaces. Quotes prevent Bash expansion)")
+
+def run_make(cmd: str ,namespace: str) -> None:
+  env = os.environ.copy()
+  env["NAMESPACE"] = namespace
+  
+  make_cmd = ["/bin/make" ,"-f" ,"tool/makefile" ,cmd]
+  
+  try:
+    subprocess.run(make_cmd ,env=env ,check=True)
+  except subprocess.CalledProcessError as e:
+    print(f"make failed for namespace '{namespace}' with exit code {e.returncode}" ,file=sys.stderr)
+    sys.exit(e.returncode)
+
+def CLI() -> None:
+  check_environment()
+  
+  repo_home = os.environ.get("REPO_HOME" ,".")
+  dev_dir = os.path.join(repo_home ,"developer")
+  os.chdir(dev_dir)
+  
+  args = sys.argv[1:]
+  
+  if( not args or args[0] in ["usage" ,"help" ,"-h" ,"--help"] ):
+    print_usage()
+    sys.exit(0)
+    
+  namespaces = get_namespaces()
+  matched_any = False
+
+  for TM_arg in args:
+    parts = TM_arg.split(":")
+    pattern = parts[0]
+    commands = parts[1:]
+    
+    if( not commands ):
+      commands = ["all"]
+      
+    matches = fnmatch.filter(namespaces ,pattern)
+
+    if( not matches ):
+      print(f"(warning) Pattern '{pattern}' matched no namespaces." ,file=sys.stderr)
+      continue
+
+    matched_any = True
+    for TM_ns in matches:
+      for TM_cmd in commands:
+        run_make(TM_cmd ,TM_ns)
+  
+  if( not matched_any ):
+    sys.exit(1)
+
+  print("make done.")
+
+if __name__ == "__main__":
+  CLI()
index 65408ed..070c6ca 100644 (file)
@@ -1,54 +1,61 @@
-# developer/tool/makefile  Orchestrator (Hybrid)
+# developer/tool/makefile - Orchestrator (Hybrid)
 .SUFFIXES:
 .EXPORT_ALL_VARIABLES:
 
-RT_INCOMMON := $(REPO_HOME)/shared/third_party/RT-project-share/release
-include $(RT_INCOMMON)/make/environment_RT_1.mk
+RT_MAKEFILE_DP := $(REPO_HOME)/shared/tool/makefile
+
+# If a namespace is provided, update the source directory
+ifneq ($(NAMESPACE),)
+  C_SOURCE_DIR := authored/$(NAMESPACE)
+  export C_SOURCE_DIR
+endif
 
 .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:
        @printf "local ----------------------------------------\n"
        @echo tool/makefile version 2.0
-       @printf "target_library_CLI.mk ----------------------------------------\n"
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_kmod.mk version
-       @printf "target_kmod.mk ----------------------------------------\n"
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_library_CLI.mk version
+       @printf "Harmony.mk ----------------------------------------\n"
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk version
+       @printf "target_kernel-module.mk ----------------------------------------\n"
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk version
 
 .PHONY: information
 information:
        @printf "local ----------------------------------------\n"
        -@echo CURDIR='$(CURDIR)'
        @echo REPO_HOME="$(REPO_HOME)"
+       @echo NAMESPACE="$(NAMESPACE)"
+       @echo C_SOURCE_DIR="$(C_SOURCE_DIR)"
        @echo KMOD_BUILD_DIR="/lib/modules/$(shell uname -r)/build"
        @echo CURDIR="$(CURDIR)"
-       @printf "target_library_CLI.mk ----------------------------------------\n"
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_library_CLI.mk information
-       @printf "target_kmod.mk ----------------------------------------\n"
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_kmod.mk information
+       @printf "Harmony.mk ----------------------------------------\n"
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk information
+       @printf "target_kernel-module.mk ----------------------------------------\n"
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk information
 
 .PHONY: all
 all: library CLI kmod
 
 .PHONY: library lib
 library lib:
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_library_CLI.mk library
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk library
 
 .PHONY: CLI
 CLI:
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_library_CLI.mk CLI
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk CLI
 
 .PHONY: kmod
 kmod:
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_kmod.mk kmod
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk kmod
 
 .PHONY: clean
 clean:
        @printf "local ----------------------------------------\n"
-       @printf "target_library_CLI.mk ----------------------------------------\n"
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_library_CLI.mk clean
-       @printf "target_kmod.mk ----------------------------------------\n"
-       @$(MAKE) -f $(RT_INCOMMON)/make/target_kmod.mk clean
+       @printf "Harmony.mk ----------------------------------------\n"
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk clean
+       @printf "target_kernel-module.mk ----------------------------------------\n"
+       @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk clean
diff --git a/developer/tool/release b/developer/tool/release
deleted file mode 100755 (executable)
index 24f260c..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-import os ,sys ,shutil ,stat ,pwd ,grp ,glob ,tempfile
-
-HELP = """usage: release {write|clean|ls|diff|help|dry write}
-  write       Writes promoted files from scratchpad/stage into consumer/release. Only updates newer files.
-  clean       Remove all contents of the consumer/release directory.
-  ls          List consumer/release as an indented tree: PERMS  OWNER  NAME.
-  diff        List files in consumer/release that are not in scratchpad/stage, or are newer.
-  help        Show this message.
-  dry write   Preview what write would do without modifying the filesystem.
-"""
-
-SETUP_MUST_BE = "developer/tool/setup"
-DEFAULT_DIR_MODE = 0o700
-DEFAULT_FILE_MODE = 0o400
-
-def exit_with_status(msg ,code=1):
-  print(f"release: {msg}" ,file=sys.stderr)
-  sys.exit(code)
-
-def assert_setup():
-  setup_val = os.environ.get("SETUP" ,"")
-  if(setup_val != SETUP_MUST_BE):
-    hint = (
-      "SETUP is not 'developer/tool/setup'.\n"
-      "Enter the project with:  . setup developer\n"
-      "That script exports: ROLE=developer; SETUP=$ROLE/tool/setup"
-    )
-    exit_with_status(f"bad environment: SETUP='{setup_val}'. {hint}")
-
-def repo_home():
-  rh = os.environ.get("REPO_HOME")
-  if( not rh ):
-    exit_with_status("REPO_HOME not set")
-  return rh
-
-def dpath(*parts):
-  return os.path.join(
-    repo_home()
-    ,"developer"
-    ,*parts
-  )
-
-def cpath(*parts):
-  return os.path.join(
-    repo_home()
-    ,"consumer"
-    ,"release"
-    ,*parts
-  )
-
-def dev_root():
-  return dpath()
-
-def consumer_root():
-  return cpath()
-
-def _display_src(p_abs: str) -> str:
-  try:
-    if( os.path.commonpath([dev_root()]) == os.path.commonpath([dev_root() ,p_abs]) ):
-      return os.path.relpath(p_abs ,dev_root())
-  except Exception:
-    pass
-  return p_abs
-
-def _display_dst(p_abs: str) -> str:
-  try:
-    rel = os.path.relpath(
-      p_abs
-      ,consumer_root()
-    )
-    rel = "" if rel == "." else rel
-    return "$REPO_HOME/consumer/release" + ("/" + rel if rel else "")
-  except Exception:
-    return p_abs
-
-def ensure_mode(path_str ,mode):
-  try: os.chmod(path_str ,mode)
-  except Exception: pass
-
-def ensure_dir(path_str ,mode=DEFAULT_DIR_MODE ,dry=False):
-  if(dry):
-    if( not os.path.isdir(path_str) ):
-      shown = _display_dst(path_str) if path_str.startswith(consumer_root()) else (
-        os.path.relpath(path_str ,dev_root()) if path_str.startswith(dev_root()) else path_str
-      )
-      print(f"(dry) mkdir -m {oct(mode)[2:]} '{shown}'")
-    return
-  os.makedirs(path_str ,exist_ok=True)
-  ensure_mode(path_str ,mode)
-
-def filemode(m):
-  try: return stat.filemode(m)
-  except Exception: return oct(m & 0o777)
-
-def owner_group(st):
-  try: return f"{pwd.getpwuid(st.st_uid).pw_name}:{grp.getgrgid(st.st_gid).gr_name}"
-  except Exception: return f"{st.st_uid}:{st.st_gid}"
-
-def list_tree(root_dp):
-  if( not os.path.isdir(root_dp) ):
-    return
-
-  entries = []
-  def gather(path_str ,depth ,is_root):
-    try:
-      it = list(os.scandir(path_str))
-    except FileNotFoundError:
-      return
-    dirs = [e for e in it if e.is_dir(follow_symlinks=False)]
-    files = [e for e in it if not e.is_dir(follow_symlinks=False)]
-    dirs.sort(key=lambda e: e.name)
-    files.sort(key=lambda e: e.name)
-
-    if(is_root):
-      for f in ( e for e in files if e.name.startswith(".") ):
-        st = os.lstat(f.path)
-        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
-      for d in dirs:
-        st = os.lstat(d.path)
-        entries.append((True ,depth ,filemode(st.st_mode) ,owner_group(st) ,d.name + "/"))
-        gather(d.path ,depth + 1 ,False)
-      for f in ( e for e in files if not e.name.startswith(".") ):
-        st = os.lstat(f.path)
-        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
-    else:
-      for d in dirs:
-        st = os.lstat(d.path)
-        entries.append((True ,depth ,filemode(st.st_mode) ,owner_group(st) ,d.name + "/"))
-        gather(d.path ,depth + 1 ,False)
-      for f in files:
-        st = os.lstat(f.path)
-        entries.append((False ,depth ,filemode(st.st_mode) ,owner_group(st) ,f.name))
-
-  gather(root_dp ,1 ,True)
-
-  ogw = 0
-  for _isdir ,_depth ,_perms ,ownergrp ,_name in entries:
-    if( len(ownergrp) > ogw ):
-      ogw = len(ownergrp)
-
-  print("consumer/release/")
-  for isdir ,depth ,perms ,ownergrp ,name in entries:
-    indent = "  " * depth
-    print(f"{perms}  {ownergrp:<{ogw}}  {indent}{name}")
-
-def copy_one(src_abs ,dst_abs ,mode ,dry=False):
-  src_show = _display_src(src_abs)
-  dst_show = _display_dst(dst_abs)
-  parent = os.path.dirname(dst_abs)
-  os.makedirs(parent ,exist_ok=True)
-
-  if(dry):
-    if( os.path.exists(dst_abs) ):
-      print(f"(dry) unlink '{dst_show}'")
-    print(f"(dry) install -m {oct(mode)[2:]} -D '{src_show}' '{dst_show}'")
-    return
-
-  fd ,tmp_path = tempfile.mkstemp(prefix=".tmp." ,dir=parent)
-  try:
-    with os.fdopen(fd ,"wb") as tmpf, open(src_abs ,"rb") as sf:
-      shutil.copyfileobj(sf ,tmpf)
-      tmpf.flush()
-    os.chmod(tmp_path ,mode)
-    os.replace(tmp_path ,dst_abs)
-  finally:
-    try:
-      if( os.path.exists(tmp_path) ):
-        os.unlink(tmp_path)
-    except Exception:
-      pass
-
-  print(f"+ install -m {oct(mode)[2:]} '{src_show}' '{dst_show}'")
-
-def cmd_write(dry=False):
-  assert_setup()
-  ensure_dir(cpath() ,DEFAULT_DIR_MODE ,dry=dry)
-
-  src_root = dpath(
-    "scratchpad"
-    ,"stage"
-  )
-  if( not os.path.isdir(src_root) ):
-    exit_with_status(f"cannot find developer scratchpad stage at '{_display_src(src_root)}'")
-
-  wrote = False
-  for root ,dirs ,files in os.walk(src_root):
-    dirs.sort()
-    files.sort()
-    for fn in files:
-      src_abs = os.path.join(root ,fn)
-      rel = os.path.relpath(src_abs ,src_root)
-      dst_abs = os.path.join(cpath() ,rel)
-
-      if( os.path.exists(dst_abs) ):
-        src_mtime = os.stat(src_abs).st_mtime
-        dst_mtime = os.stat(dst_abs).st_mtime
-        if(dst_mtime >= src_mtime):
-          continue
-
-      st = os.stat(src_abs)
-      is_exec = st.st_mode & stat.S_IXUSR
-      mode = 0o500 if is_exec else 0o400
-
-      copy_one(
-        src_abs
-        ,dst_abs
-        ,mode
-        ,dry=dry
-      )
-      wrote = True
-
-  if( not wrote ):
-    print(f"(info) nothing new to promote from {_display_src(src_root)}")
-
-def cmd_diff():
-  assert_setup()
-  dst_root = cpath()
-  src_root = dpath(
-    "scratchpad"
-    ,"stage"
-  )
-
-  if( not os.path.isdir(dst_root) ):
-    print(f"Release directory {_display_dst(dst_root)} does not exist.")
-    return
-
-  found_diff = False
-  for root ,dirs ,files in os.walk(dst_root):
-    dirs.sort()
-    files.sort()
-    for fn in files:
-      dst_abs = os.path.join(root ,fn)
-      rel = os.path.relpath(dst_abs ,dst_root)
-      src_abs = os.path.join(src_root ,rel)
-
-      if( not os.path.exists(src_abs) ):
-        print(f"Orphaned in release: {rel}")
-        found_diff = True
-      else:
-        dst_mtime = os.stat(dst_abs).st_mtime
-        src_mtime = os.stat(src_abs).st_mtime
-        if(dst_mtime > src_mtime):
-          print(f"Newer in release: {rel}")
-          found_diff = True
-
-  if( not found_diff ):
-    print("No differences found. Release matches stage.")
-
-def cmd_clean():
-  assert_setup()
-  consumer_root_dir = cpath()
-  if( not os.path.isdir(consumer_root_dir) ):
-    return
-  for name in os.listdir(consumer_root_dir):
-    p = os.path.join(consumer_root_dir ,name)
-    if( os.path.isdir(p) and not os.path.islink(p) ):
-      shutil.rmtree(p ,ignore_errors=True)
-    else:
-      try: os.unlink(p)
-      except FileNotFoundError: pass
-
-def CLI():
-  if( len(sys.argv) < 2 ):
-    print(HELP)
-    return
-  cmd ,*args = sys.argv[1:]
-  if(cmd == "write"):
-    cmd_write(dry=False)
-  elif(cmd == "clean"):
-    cmd_clean()
-  elif(cmd == "ls"):
-    list_tree(cpath())
-  elif(cmd == "diff"):
-    cmd_diff()
-  elif(cmd == "help"):
-    print(HELP)
-  elif(cmd == "dry"):
-    if( args and args[0] == "write" ):
-      cmd_write(dry=True)
-    else:
-      print(HELP)
-  else:
-    print(HELP)
-
-if __name__ == "__main__":
-  CLI()
diff --git a/document/introduction_Harmony.html b/document/introduction_Harmony.html
new file mode 100644 (file)
index 0000000..7d35941
--- /dev/null
@@ -0,0 +1,380 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>Introduction to Harmony</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="Thomas Walker Lynch" 
+        date="2026-03-09" 
+        title="Introduction to Harmony">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <h1>Purpose</h1>
+      <p>
+        Harmony provides a language-agnostic project directory structure and maintenance tools for long-lived, multi-person team software development. The structure exists to enforce:
+      </p>
+      <ol>
+        <li>Clarity about where things live.
+          <ul>
+            <li>Role-based work areas.</li>
+            <li>Separation of skeleton, team member authored, machine-made, and third-party installed software.</li>
+          </ul>
+        </li>
+        <li>A safe, predictable build and promotion workflow.</li>
+      </ol>
+
+      <p>
+        Harmony is a project skeleton, a set of tools and directories that have been checked into a git repository. To create a Harmony-based project, the project administrator performs these steps:
+      </p>
+      <ol>
+        <li>Clone the Harmony project to a local directory.</li>
+        <li>Remove the <RT-code>.git</RT-code> tree.</li>
+        <li>Rename the Harmony directory to the name of the new project.</li>
+        <li>Rename the <RT-code>0pus_Harmony</RT-code> file to reflect the name of the new project.</li>
+        <li>Add a line to the <RT-code>shared/tool/version</RT-code> file for the new project.</li>
+        <li><RT-code>git init -b core_developer_branch</RT-code></li>
+      </ol>
+
+      <p>
+        The order is important so as to protect the Harmony skeleton project from accidental check-ins from users.
+      </p>
+      <p>
+        Leave the Harmony skeleton version in the version file, so that project administrators can understand what has changed in the skeleton over time.
+      </p>
+      <p>
+        The <RT-code>core_developer_branch</RT-code> is the branch that the core development team works on. Project releases are moved to release branches.
+      </p>
+
+      <h1>Environment setup</h1>
+      <p>
+        Python programmers who use virtual environments will be familiar with an analogous process. The term 'virtual environment' does not invoke any hardware virtualization features; rather, it is a local environment setup. To avoid confusion, a Harmony user refers instead to the 'project setup'. The command to establish the environment is called 'setup' instead of 'activate'. Also note that in Harmony, unlike in Python, there are multiple setups available, each tailored to the specific role a person takes on.
+      </p>
+      <p>
+        As of the time of this writing, the defined roles are: <strong>administrator</strong>, <strong>consumer</strong>, <strong>developer</strong>, and <strong>tester</strong>. A person takes on a role by sourcing the top-level <RT-code>setup</RT-code> script and giving the target role as an argument. For example, in a bash shell with <RT-code>></RT-code> as the prompt, the command is:
+      </p>
+
+      <RT-code>
+        > . setup &lt;role&gt;
+      </RT-code>
+
+      <p>
+        Specifically for the developer role:
+      </p>
+        
+      <RT-code>
+        > . setup developer
+      </RT-code>
+
+      <p>
+        For the administrator role:
+      </p>
+        
+      <RT-code>
+        > . setup administrator
+      </RT-code>
+
+      <p>
+        Instead of starting with a period, the <RT-code>source</RT-code> command can be spelled out explicitly, for example:
+      </p>
+      
+      <RT-code>
+        > source setup tester
+      </RT-code>
+
+      <p>
+        Behind the scenes, the <RT-code>setup</RT-code> script performs the following actions:
+      </p>
+
+      <ul>
+        <li>Sources the project-wide setup (<RT-code>shared/tool/setup</RT-code>) to establish the core environment variables (e.g., <RT-code>REPO_HOME</RT-code> and <RT-code>PROJECT</RT-code>).</li>
+        <li>Conditionally sources <RT-code>shared/authored/setup</RT-code> (if present) to apply administrator-injected, project-specific tool configurations.</li>
+        <li>Configures the <RT-code>PATH</RT-code> to include shared tools, library environments, and the specific <RT-code>&lt;role&gt;/tool</RT-code> directory.</li>
+        <li>Changes the working directory into the specified role's workspace.</li>
+        <li>Sources the <RT-code>&lt;role&gt;/tool/setup</RT-code> script. While the earlier steps apply the standard Harmony skeleton setup, this final step applies the role setup that is customized for this specific project.</li>
+      </ul>
+
+      <h1>After git clone</h1>
+      <p>
+        Because git does not track certain directories (such as <RT-code>shared/linked-project/</RT-code> and <RT-code>consumer/made/</RT-code>), a freshly cloned repository lacks external dependencies and consumable products. Team members must perform a few steps to populate these areas.
+      </p>
+
+      <h2>Third-party tools</h2>
+      <p>
+        Harmony is language agnostic. When a project makes use of project-specific C, Python, NodeJS, Java, or other tools, the project administrator configures the project to expect these tools in the <RT-code>shared/linked-project</RT-code> directory.
+      </p>
+      <p>
+        Because multiple team members will have to repeat the third-party install process after cloning a project, the administrator should carefully document the third party tools installation steps and place the resulting documents in the <RT-code>shared/document</RT-code> directory. (The most common installation method is to clone the third party tool next to the project, then to symbolic link it under <RT-code>shared/linked-project/</RT-code>.)
+      </p>
+      <p>
+        If a person clones the project and does not set up third-party tools, commands will fall through the search path and perhaps find system-installed tools. Using system default tools is generally undesirable because each person who clones the project might be running different versions. This variation makes it harder for team members to coordinate and for consumers of the project work product to build functioning tools.
+      </p>
+
+      <h2>Consumer build</h2>
+      <p>
+        In this section we use the term 'consumer' to mean any team member that wants to make use of the project work product. The tester will want to test it, and the consumer role will want to deploy it, etc.
+      </p>
+      <p>
+        An earlier version of Harmony used platform-specific <RT-code>made</RT-code> directories, but this left binaries in the repository and added excessive complexity.
+        So as to avoid that mess, the current version of Harmony requires a work product consumer to run a local build after cloning the project. The results of the build will appear in the <RT-code>consumer/made</RT-code> directory.
+      </p>
+      <p>
+        To facilitate this, the developer must explicitly document the project's build and promote procedure, saving this guide as <RT-code>developer/document/build.html</RT-code>. 
+      </p>
+      <p>
+        The consumer must then read this document and execute the described steps to compile the source and locally populate their <RT-code>consumer/made</RT-code> directory.
+      </p>
+
+      <p>
+        Because the build and promotion tools are strictly isolated within the developer workspace, the consumer must temporarily put on the developer hat to perform the build. Typically the consumer build procedure will be a variation of the following:
+      </p>
+
+      <ol>
+        <li><RT-code>> bash</RT-code></li>
+        <li><RT-code>> cd &lt;project&gt;</RT-code></li>
+        <li><RT-code>> . setup developer</RT-code></li>
+        <li><RT-code>> make CLI</RT-code></li>
+        <li><RT-code>> promote write</RT-code></li>
+        <li><RT-code>> exit</RT-code></li>
+        <li><RT-code>> bash</RT-code></li>
+        <li><RT-code>> cd &lt;project&gt;</RT-code></li>
+        <li><RT-code>> . setup &lt;role&gt;</RT-code></li>
+      </ol>
+
+      <p>
+        This sequence opens a bash shell, assumes the developer role to orchestrate the build, makes the work product, then promotes it to the consumer's workspace. The <RT-code>exit</RT-code> command drops the developer role. The last two lines put the person into the &lt;role&gt; workspace, typically for testing or deploying. Commonly, deployment is a matter of adding the <RT-code>consumer/made</RT-code> directory into the executable search path.
+      </p>
+
+
+      <h1>Directory semantic properties</h1>
+      <p>
+        This section discusses our thinking in naming the files and directories found in the Harmony skeleton.
+      </p>
+      <p>
+        A directory name is considered to be a <RT-term>property</RT-term> given to each file contained in the directory. A full path then forms a semantic sentence describing each file.
+      </p>
+      <p>
+        Because a directory name represents a property, it is rarely plural. For example, when each and every file in a directory is a test, the directory is named <RT-code>test</RT-code>.
+      </p>
+      <p>
+        We run into limitations when using a conventional file system as though it were a property based file system. One limitation is that we are forced to choose a single directory name for each file. When a set of files in a directory all share the same multiple properties, we can use a compound directory name with the properties separated by an underscore, but it is impractical to specify overlapping directory groupings, i.e. we can't arbitrarily define any number of properties for a file in this manner.
+      </p>
+      <p>
+        The following list presents each property type in order of preference when naming directories:
+      </p>
+      <ul>
+        <li><strong>Role Association</strong> (<RT-code>administrator</RT-code>, <RT-code>developer</RT-code>, <RT-code>tester</RT-code>, <RT-code>consumer</RT-code>): Identifies the persona, whether human or AI, intended to interact with the files.</li>
+        <li><strong>Provenance</strong> (<RT-code>authored</RT-code>, <RT-code>made</RT-code>): Indicates whether the file was created by an intellect or mechanically produced by a tool.</li>
+        <li><strong>Capability</strong> (<RT-code>tool</RT-code>, <RT-code>document</RT-code>, <RT-code>experiment</RT-code>): Describes the primary function or structural nature of the file.</li>
+        <li><strong>Lifecycle State</strong> (<RT-code>scratchpad</RT-code>, <RT-code>stage</RT-code>): Denotes the persistence, volatility, or promotion status of the file.</li>
+        <li><strong>Tracking Status</strong> (<RT-code>tracked</RT-code>, <RT-code>untracked</RT-code>): Specifies the version control expectations for the artifacts.</li>
+      </ul>
+
+      <h2>Authored, made, scratchpad, inherited</h2>
+      <p>
+        Files found in a directory named <RT-code>authored</RT-code> were written by project team members. They did not come with the Harmony skeleton, nor with the installation of other software. Project build tools treat <RT-code>authored</RT-code> directories as strictly read-only. Typically these files constitute the intellectual property of a project.
+      </p>
+      <p>
+        All source code that gets built into a promotion or project release must be placed in the developers' <RT-code>authored</RT-code> directory. The story is not as clean for build tools and other files. New documents go into document directories, and new tools go into the tool directories, etc. As a specific example, the developer will almost certainly edit the <RT-code>developer/tool/make</RT-code> file.
+      </p>
+      <p>
+        When the Harmony version line in the <RT-code>shared/tool/version</RT-code> file is left in place, it is straightforward for a project administrator to determine which Harmony skeleton files have been edited in a project, and which new files have been added.
+      </p>
+      <p>
+        Files found in a directory named <RT-code>scratchpad</RT-code> are not tracked. Hence, a <RT-code>git clone</RT-code> will always return empty <RT-code>scratchpad</RT-code> directories. It is common for tools to place intermediate files on a scratchpad. It is also common for files to be staged on a scratchpad. Tools play nice and use subdirectories on the pad, so a person who is aware of those subdirectory names can use a scratchpad as a temporary directory. There is a scratchpad maintenance tool that comes with the Harmony, called unimaginatively, <RT-code>scratchpad</RT-code>. Pay attention as one of its commands is <RT-code>clear</RT-code>, and that deletes everything on the current directory's scratchpad.
+      </p>
+      <p>
+        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>.
+      </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>.
+      </p>
+
+      <ul>
+        <li><RT-code>administrator/</RT-code> : Project-local tools and skeleton maintenance.</li>
+        <li><RT-code>developer/</RT-code> : Primary workspace for developers.</li>
+        <li><RT-code>tester/</RT-code> : Regression and validation workspace for testers.</li>
+        <li><RT-code>consumer/</RT-code> : Consumption workspace holding the <RT-code>consumer/made</RT-code> directory.</li>
+        <li><RT-code>shared/</RT-code> : Shared ecosystem tools and global environments.</li>
+      </ul>
+
+      <h2>The administrator work area</h2>
+      <p>
+        This directory holds the tools and documentation used to manage the project as a whole. It includes the HTML documentation for the project ontology and workflow, as well as project-local tools utilized by the administrator to maintain the Harmony skeleton.
+      </p>
+
+      <h2>The developer work area</h2>
+      <p>
+        This directory is entered by first going to the top-level directory of the project, then sourcing <RT-code>. setup developer</RT-code>.
+      </p>
+      <ul>
+        <li><RT-code>authored/</RT-code> : Human-written source. Tracked by Git.</li>
+        <li><RT-code>made/</RT-code> : Tracked artifacts generated by tools (e.g., links to CLI entry points).</li>
+        <li><RT-code>experiment/</RT-code> : Try-it-here code. Short-lived spot testing.</li>
+        <li><RT-code>scratchpad/</RT-code> : Git-ignored directory. Holds all intermediate build outputs, including the <RT-code>made</RT-code> directory for promotions.</li>
+        <li><RT-code>tool/</RT-code> : Developer-specific tools (like the promote and make scripts).</li>
+      </ul>
+
+      <h2>The tester work area</h2>
+      <p>
+        This directory is dedicated to formal testing, including regression suites. While a developer can run and keep informal spot tests in their <RT-code>experiment/</RT-code> directory, any experiment promoted to a formal test is moved here. This enforces the boundary between writing code and validating it.
+      </p>
+
+      <h2>The shared tree</h2>
+      <p>
+        This directory contains ecosystem tools and global environments available to all roles. This includes the shared tool directory, as well as third-party installations (like Python virtual environments or compilers) required by the project. To assist in project specific modifications to the Harmony skeleton, Harmony comes with an empty <RT-code>shared/authored</RT-code> directory that is listed earlier in the executable search path than <RT-code>shared/tool</RT-code>.
+      </p>
+
+      <h2>The consumer tree</h2>
+      <p>
+        The <RT-code>consumer/made/</RT-code> tree is where developers put work product that is ready to be consumed. The entire <RT-code>consumer/made</RT-code> directory is git-ignored and treated as a transient deployment target.
+      </p>
+      <p>
+        Artifacts arrive in the <RT-code>consumer/made/</RT-code> tree <em>only</em> when the promote script is invoked, which performs a flat-copy from the developer's <RT-code>scratchpad/made</RT-code> directory.
+      </p>
+
+      <h2>Document directories</h2>
+      <p>
+        There is a directory for documents that talks about the project as a whole, one for each role, one for tools that are shared among the roles, and the released work product probably comes with a document directory of its own.
+      </p>
+      <ul>
+        <li><RT-code>document/</RT-code> : Top-level onboarding, project-wide structure, such as this document.</li>
+        <li><RT-code>consumer/made/document/</RT-code> : Documentation for end-users of made code (e.g., man pages, application manuals, library API references).</li>
+        <li><RT-code>administrator/document/</RT-code> : Documentation for maintaining the project skeleton and global tools.</li>
+        <li><RT-code>developer/document/</RT-code> : Documentation for developers, including coding standards and internal API guides.</li>
+        <li><RT-code>tester/document/</RT-code> : Documentation for testers detailing test plans and tools.</li>
+        <li><RT-code>shared/document/</RT-code> : Documentation on installing and configuring shared tools.</li>
+      </ul>
+
+      <p>
+        Note that the <RT-code>consumer/made</RT-code> directory is untracked by git and maintained by a tool. (Said tool is <RT-code>developer/tool/promote</RT-code>. It is owned and used by the developer as part of the build process.) Documents that are being promoted for eventual release, and appear in the <RT-code>made</RT-code> directory originate from somewhere from the <RT-code>developer/authored</RT-code> directory depending on how the developer has organized his workspace, but probably in <RT-code>developer/authored/document</RT-code>. Perhaps a future version of Harmony will have a tech-writer role, but that is not the case now.
+      </p>
+
+      <p>
+        Currently, our developers write documents directly in HTML using the RT semantic tags. See the <RT-code>RT-style-JS_public</RT-code> project and the documentation there. A common approach is to copy another document and the <RT-code>setup.js</RT-code> file, then to type over the top of that other document. Only one <RT-code>setup.js</RT-code> file is used per directory. Be sure to edit the relative root path found at the top of <RT-code>setup.js</RT-code>. Plain text, emacs org, and mark down have all been used in prior versions of Harmony.
+      </p>
+
+      <h2>Untracked directories</h2>
+      <ol>
+        <li><RT-code>consumer/made/</RT-code></li>
+        <li><RT-code>shared/linked-project/</RT-code></li>
+        <li><RT-code>**/scratchpad/</RT-code></li>
+      </ol>
+
+      <h1>Workflow</h1>
+      <p> See the document "Product Development Roles and Workflow" for more details.</p>
+
+      <h1>Developer promotion and project releases</h1>
+      <p>
+        As a first step, a developer creates a <RT-term>promotion candidate</RT-term> inside of the <RT-code>consumer/made/</RT-code> directory. This is typically done by running <RT-code>make</RT-code> to stage the artifacts into <RT-code>scratchpad/made</RT-code>, where they can be experimented on, followed by running <RT-code>promote write</RT-code>.  The developer will often modify the versions of one or both of those tools that come with the Harmony skeleton. The promotion candidate remains stable until the next <RT-term>promotion</RT-term>.
+      </p>
+      <p>
+        Then the tester runs tests on the promotion candidate. Tests must only read from the <RT-code>consumer/made/</RT-code> directory, though local copies can be made and edited as experiments. Currently bugs are filed using an external issues tool.
+      </p>
+      <p>
+        It is common for a developer to open a second window on his desktop, and then enter the project as a tester in that second window. The developer can then make a promotion candidate, run the tests, edit source code, and perhaps tests, and then quickly spin through the test-debug-fix-promote cycle repeatedly.
+      </p>
+      <p>
+        When the product manager determines the work product to be sufficiently reliable and feature rich, the administrator will make a <RT-term>project release</RT-term>. He will do this by creating a branch called <RT-code>release_v&lt;major&gt;</RT-code> and tagging it. The major release numbers go up incrementally.
+      </p>
+
+      <h1>The version 2.2 Harmony directory tree</h1>
+
+      <RT-code>
+      2026-03-09 01:42:16 Z [Harmony:administrator] Thomas_developer@StanleyPark
+      §/home/Thomas/subu_data/developer/project§
+      > tree Harmony
+      Harmony
+      ├── 0pus_Harmony
+      ├── administrator
+      │ ├── authored
+      │ ├── document
+      │ │ └── setup.js
+      │ └── tool
+      │   ├── archive
+      │   └── setup
+      ├── consumer
+      │ ├── scratchpad
+      │ └── tool
+      │   └── env
+      ├── developer
+      │ ├── authored
+      │ │ └── hello.CLI.c
+      </RT-code>
+      <RT-code>
+      │ ├── document
+      │ │ ├── 02_RT_Code_Format.html
+      │ │ ├── 03_Naming_and_Directory_Conventions.html
+      │ │ ├── 04_Language_Addenda.html
+      │ │ └── setup.js
+      │ ├── experiment
+      │ ├── made
+      │ ├── scratchpad
+      │ └── tool
+      │   ├── do_all
+      │   ├── make
+      │   ├── makefile
+      │   ├── promote
+      │   └── setup
+      ├── document
+      │ ├── introduction_Harmony.html
+      │ ├── role-and-workflow_product-development.html
+      │ └── setup.js
+      ├── LICENSE
+      </RT-code>
+      <RT-code>
+      ├── README.md
+      ├── scratchpad
+      │ ├── Harmony__79f9d52__2026-03-07_085628Z.tar
+      │ └── Harmony__e665bb7__2026-03-09_013712Z.tar
+      ├── setup
+      ├── shared
+      │ ├── authored
+      │ ├── document
+      │ │ ├── installation_generic.org
+      │ │ ├── installation_Python.org
+      │ │ └── setup.js
+      │ ├── made
+      │ ├── dictionary_style-directory.js
+      │ ├── third_party
+      │ │ ├── RT-style-JS_public -> ../../../RT-style-JS_public/
+      │ │ └── upstream
+      │ └── tool
+      │   ├── scratchpad
+      │   ├── setup
+      </RT-code>
+      <RT-code>
+      │   ├── style
+      │   └── version
+      └── tester
+          ├── authored
+          │ └── test_routine.sh
+          ├── RT_Format
+          │ ├── RT_Format
+          │ ├── RT-formatter.el
+          │ ├── data_test-0.c
+          │ └── data_test-1.py
+          └── tool
+              └── setup
+
+      30 directories, 36 files
+
+      2026-03-09 01:42:19 Z [Harmony:administrator] Thomas_developer@StanleyPark
+      §/home/Thomas/subu_data/developer/project§
+      > 
+      </RT-code>
+
+    </RT-article>
+  </body>
+</html>
diff --git a/document/role-and-workflow_product-development.html b/document/role-and-workflow_product-development.html
new file mode 100644 (file)
index 0000000..149624b
--- /dev/null
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>Product development roles and workflow</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="Thomas Walker Lynch" 
+        date="2026-03-09" 
+        title="Product development roles and workflow">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <h1>Roles as hats</h1>
+      <p>
+        In Harmony, a role is a hat a person wears. There can be multiple people sharing a single role, and a single person can wear many hats. Some roles interact directly with the project directory structure, while others guide the process from outside the codebase. 
+      </p>
+
+      <h2>Workspace roles</h2>
+      <p>
+        These roles interact directly with the repository. To enter a workspace, change directory to the top-level of the project and source the setup file for the desired role:
+      </p>
+      <RT-code>
+        > . 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>
+
+      <h3>Administrator role</h3>
+      <p>Responsibilities:</p>
+      <ol>
+        <li>Set up the project directory and keep it in sync with the Harmony skeleton.</li>
+        <li>Maintain role environments (apart from role-specific <RT-code>tool/setup</RT-code> files).</li>
+        <li>Install and maintain shared and third_party tools, addressing issues with the project workflow. Note that the term "third_party" encompasses any software not authored within this specific project.</li>
+      </ol>
+
+      <h3>Developer role</h3>
+      <p>Responsibilities and Boundaries:</p>
+      <ol>
+        <li>Write and modify <RT-code>authored/</RT-code> source.</li>
+        <li>Run builds and place artifacts in <RT-code>scratchpad/made</RT-code>, then execute the <RT-code>promote write</RT-code> script to copy artifacts to <RT-code>consumer/made</RT-code> for testing.</li>
+        <li>Run experiments in <RT-code>experiment/</RT-code>. These experiments can sometimes be promoted to formal tests, but there is no requirement to do so. The developer role should not blur into the tester role; experiments are informal, whereas tests are formal and retained.</li>
+        <li><strong>Strict Boundary:</strong> A developer never writes into the <RT-code>tester/</RT-code> directory. Instead, a developer adds tests to <RT-code>developer/experiment/</RT-code> and offers to share them.</li>
+      </ol>
+
+      <h3>Tester role</h3>
+      <p>Responsibilities and Boundaries:</p>
+      <ol>
+        <li>Evaluate candidates under <RT-code>consumer/made/</RT-code> and run regression suites to confirm:
+          <ul>
+            <li>That the code does not crash.</li>
+            <li>That consumers do not have a bad experience.</li>
+            <li>That the goals specified by the product manager are met.</li>
+          </ul>
+        </li>
+        <li>File issues and communicate feedback to the developers.</li>
+        <li><strong>Strict Boundary:</strong> A tester never patches code in the <RT-code>developer/</RT-code> directory. Instead, the tester files issues or proposes code fixes on a separate branch.</li>
+      </ol>
+
+      <h3>Consumer role</h3>
+      <p>Responsibilities:</p>
+      <ol>
+        <li>Act as the end-user simulation environment.</li>
+        <li>Consume and deploy artifacts exclusively from the <RT-code>consumer/made/</RT-code> target.</li>
+        <li>Never author or modify code; strictly run local builds or deployments for architecture-specific testing.</li>
+        <li>Report issues. (Anyone can report an issue, and consumers regularly do).</li>
+      </ol>
+
+      <h2>External roles</h2>
+      <p>
+        These roles drive the project forward but do not have a dedicated <RT-code>setup &lt;role&gt;</RT-code> workspace, as they do not directly build or test the code in that capacity. If these individuals need to interface with the code, they simply put on a workspace role hat, such as administer, developer, tester, or consumer.
+      </p>
+
+      <h3>Product manager</h3>
+      <p>
+        The product manager receives specifications from the architect and sets the specific goals for a release. When the project manager provides updates indicating readiness, the product manager makes the final decision to cut a project release.
+      </p>
+
+      <h3>Project manager</h3>
+      <p>
+        The project manager owns the schedule (such as the Gantt chart) and monitors progress. They coordinate code reviews, read issues and announcements, reformulate the schedule, and instruct the product manager. When parts of the codebase are outsourced, the project manager serves as the primary point of contact with the external teams.
+      </p>
+
+      <h3>Architect</h3>
+      <p>
+        The architect writes the original specification, sets the technical direction, and performs code reviews. The architect might be a contractor or an in-house team member.
+      </p>
+
+      <h1>The four interacting loops</h1>
+      <p>
+        The project moves forward through the continuous interaction of four distinct operational cycles.
+      </p>
+      
+      <ol>
+        <li><strong>Developer Loop:</strong> Write code, compile to the scratchpad made directory, promote to the consumer made directory, announce the promotion to the tester, read issues, and repeat.</li>
+        <li><strong>Tester Loop:</strong> Read developer announcements, read the consumer made directory, run tests, and file issues.</li>
+        <li><strong>Product Manager Loop:</strong> Provide specifications to the developer, receive progress updates from the project manager, and instruct the project administrator to make new git branch project releases and deploy.</li>
+        <li><strong>Project Manager Loop:</strong> Own the Gantt chart, coordinate code reviews, read issues and announcements, reformulate the Gantt chart, and instruct the product manager.</li>
+      </ol>
+
+      <h1>Promotion mechanics</h1>
+      <p>
+        Building and promotion are separate activities. The developer compiles and places files in <RT-code>developer/scratchpad/made</RT-code>. The developer then runs <RT-code>promote write</RT-code> to transfer those files to <RT-code>consumer/made</RT-code>. 
+      </p>
+      <p>
+        The <RT-code>consumer/made</RT-code> directory is strictly an untracked deployment target. No tools are permitted to rebuild during promotion, and no builds are run directly inside the consumer made directory.
+      </p>
+
+    </RT-article>
+  </body>
+</html>
diff --git a/document/role-and-workflow_product-maintenance.html b/document/role-and-workflow_product-maintenance.html
new file mode 100644 (file)
index 0000000..6063077
--- /dev/null
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>Product maintenance roles and workflow</title>
+    <script src="setup.js"></script>
+    <script>
+      window.StyleRT.include('RT/theme');
+      window.StyleRT.include('RT/layout/article_tech_ref');
+    </script>
+  </head>
+  <body>
+    <RT-article>
+      <RT-title 
+        author="Thomas Walker Lynch" 
+        date="2026-03-10" 
+        title="Product maintenance roles and workflow">
+      </RT-title>
+
+      <RT-TOC level="1"></RT-TOC>
+
+      <h1>Maintenance philosophy</h1>
+      <p>
+        Deployed software must be a usable quality product for the customer, beyond that we favor addressing issues in new releases, and encourage customers who are having issues to upgrade. This approach is purely one of keeping the maintenance problem tractable.
+      </p>
+
+      <h1>Maintenance structure</h1>
+      <p>
+        Teams mentioned in this document:
+      </p>
+      <ol>
+        <li>tester team</li>
+        <li>core developer team</li>
+        <li>branch maintenance team</li>
+        <li>triage team</li>
+      </ol>
+
+      <p>
+        Team membership is a role a person takes on. Multiple people can share a single role, and one person might take on multiple roles.
+      </p>
+
+      <p>
+        The tester team develops and maintains the <RT-term>regression suite</RT-term>, the <RT-term>reliability suite</RT-term>, and <RT-term>additional tests</RT-term>.
+      </p>
+
+      <p>
+        The core developer team writes the source code and compiles promotion candidates.
+      </p>
+
+      <p>
+        The branch maintenance team is typically a subset of the core developer team. This group is dedicated to applying approved fixes to specific release branches.
+      </p>
+
+      <p>
+        The triage team evaluates defects reported against active releases. The team usually includes a developer contact, the project manager, and the product manager.
+      </p>
+
+      <h1>Issue queues</h1>
+      <p>
+        The project maintains two primary issue queues:
+      </p>
+      <ol>
+        <li>released product</li>
+        <li>core developer</li>
+      </ol>
+
+      <p>
+        There can be additional queues for multiple branches of development, and for experiments.
+      </p>
+
+      <h2>Core developer queue</h2>
+      <p>
+        This queue serves the <RT-code>core_developer_branch</RT-code>. A <RT-term>tester</RT-term> writes a test for every issue reported in this queue. Doing so isolates the defect, proves the fix, and guards against future regressions.
+      </p>
+
+      <h2>Released product queue</h2>
+      <p>
+        All issues found in release branches go onto this queue initially. It tracks defects reported against deployed software across any major version. 
+      </p>
+
+      <h1>Triage and patching</h1>
+      <p>
+        Guided by the project philosophy, the triage team reviews each release queue issue to determine its impact. The team assesses whether the defect affects the <RT-code>core_developer_branch</RT-code>, assuming by default that it probably does. The team also determines if the defect is critical enough to warrant a patch on one or more active release branches.
+      </p>
+      <p>
+        Based on this assessment, the triage team files actionable tickets in the core developer queue. A ticket explicitly specifies its target branches. A ticket will be filed against either the core developer branch, specific release branches, or a combination of both.
+      </p>
+      <p>
+        Members of the tester team also file tickets directly into the core developer queue when they discover defects in the core branch.
+      </p>
+      <p>
+        Responsibility for resolving a ticket depends on its target. The core developer team addresses fixes required on the <RT-code>core_developer_branch</RT-code>. The branch maintenance team addresses fixes required on the <RT-code>release_v&lt;major&gt;</RT-code> branches.
+      </p>
+      <p>
+        When a release is patched, the branch name remains static. The administrator advances the minor release number in the <RT-code>shared/tool/version</RT-code> file and tags the commit.
+      </p>
+
+    </RT-article>
+  </body>
+</html>
diff --git a/document/todo.txt b/document/todo.txt
new file mode 100644 (file)
index 0000000..5e09e0e
--- /dev/null
@@ -0,0 +1,4 @@
+2026-03-26 03:41:44
+
+when making a skeleton from Harmony, set skeleton docs and other files not to be edited to read only
+
diff --git a/fix.py b/fix.py
new file mode 100755 (executable)
index 0000000..555cbe6
--- /dev/null
+++ b/fix.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+import os ,sys
+
+def process_file(file_path ,replacements) -> bool:
+  try:
+    with open(file_path ,"r" ,encoding="utf-8") as f:
+      content = f.read()
+  except UnicodeDecodeError:
+    return False
+
+  new_content = content
+  for old_str ,new_str in replacements:
+    new_content = new_content.replace(old_str ,new_str)
+
+  if new_content != content:
+    with open(file_path ,"w" ,encoding="utf-8") as f:
+      f.write(new_content)
+    return True
+  return False
+
+def work(root_dir: str) -> list[str]:
+  replacements = [
+    ("shared/linked-project" ,"shared/linked-project")
+    ,("linked-project/" ,"linked-project/")
+    ,("how-to_release.html" ,"how-to_release.html")
+    ,("naming_file-and-directory.html" ,"naming_file-and-directory.html")
+    ,("format_RT-code.html" ,"format_RT-code.html")
+    ,("format_RT-code.html" ,"format_RT-code.html")
+    ,("format_RT-code-Lisp.html" ,"format_RT-code-Lisp.html")
+    ,("single-file_C-module-and-namespace.html" ,"single-file_C-module-and-namespace.html")
+    ,("developer/tool/do-all" ,"developer/tool/do-all")
+    ,("introduction_Harmony.html" ,"introduction_Harmony.html")
+    ,("role-and-workflow_product-development.html" ,"role-and-workflow_product-development.html")
+    ,("role-and-workflow_product-development.html" ,"role-and-workflow_product-development.html")
+    ,("role-and-workflow_product-maintenance.html" ,"role-and-workflow_product-maintenance.html")
+    ,("installation_Python.org" ,"installation_Python.org")
+    ,("installation_generic.org" ,"installation_generic.org")
+    ,("dictionary_style-directory.js" ,"dictionary_style-directory.js")
+    ,("target_kernel-module.mk" ,"target_kernel-module.mk")
+    ,("tester/RT-formatter" ,"tester/RT-formatter")
+    ,("RT-formatter pipe" ,"RT-formatter pipe")
+    ,("RT-formatter pipe" ,"RT-formatter pipe")
+    ,("RT-formatter-buffer" ,"RT-formatter-buffer")
+    ,("RT-formatter-buffer" ,"RT-formatter-buffer")
+    ,("RT-formatter-buffer" ,"RT-formatter-buffer")
+    ,("RT-formatter-buffer" ,"RT-formatter-buffer")
+    ,("\"RTfmt\"" ,"\"RT-formatter\"")
+    ,("\"RTfmt0\"" ,"\"RT-formatter\"")
+    ,("\"RT_format\"" ,"\"RT-formatter\"")
+    ,("RT-formatter formatting" ,"RT-formatter formatting")
+    ,("RT-formatter failed" ,"RT-formatter failed")
+    ,("RT-formatter formatting" ,"RT-formatter formatting")
+    ,("RT-formatter failed" ,"RT-formatter failed")
+    ,("data_test-0.c" ,"data_test-0.c")
+    ,("data_test-1.py" ,"data_test-1.py")
+    ,("RT-formatter.el" ,"RT-formatter.el")
+    ,("RT-formatter.el" ,"RT-formatter.el")
+    ,("RT-formatter.el" ,"RT-formatter.el")
+  ]
+
+  changed_files = []
+  
+  for dirpath ,dirnames ,filenames in os.walk(root_dir):
+    path_parts = dirpath.split(os.sep)
+    if ".git" in path_parts or "scratchpad" in path_parts:
+      continue
+      
+    for fn in filenames:
+      if fn.endswith(".tar") or fn.endswith(".tar.gz") or fn.endswith(".zip"):
+        continue
+        
+      fp = os.path.join(dirpath ,fn)
+      if process_file(fp ,replacements):
+        changed_files.append(fp)
+        
+  return changed_files
+
+def CLI(argv=None) -> int:
+  root_dir = "."
+  print(f"Scanning '{root_dir}' for outdated internal references...")
+  changed = work(root_dir)
+  
+  if not changed:
+    print("No references needed updating.")
+  else:
+    print(f"Updated internal references in {len(changed)} files:")
+    for fp in changed:
+      print(f"  {fp}")
+      
+  return 0
+
+if __name__ == "__main__":
+  sys.exit(CLI())
diff --git a/next-generation-name.py b/next-generation-name.py
new file mode 100755 (executable)
index 0000000..4962355
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/env -S python3 -B
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+import os ,sys
+
+def CLI(argv=None) -> int:
+  # Ordered list of renames: files first, then directories to preserve paths
+  substitutions = [
+    # Administrator
+    ("administrator/document/how-to_release.html" ,"administrator/document/how-to_release.html")
+    
+    # Developer
+    ,("developer/document/naming_file-and-directory.html" ,"developer/document/naming_file-and-directory.html")
+    ,("developer/document/format_RT-code.html" ,"developer/document/format_RT-code.html")
+    ,("developer/document/single-file_C-module-and-namespace.html" ,"developer/document/single-file_C-module-and-namespace.html")
+    ,("developer/tool/do-all" ,"developer/tool/do-all")
+    
+    # Top-level documents
+    ,("document/introduction_Harmony.html" ,"document/introduction_Harmony.html")
+    ,("document/role-and-workflow_product-development.html" ,"document/role-and-workflow_product-development.html")
+    ,("document/role-and-workflow_product-maintenance.html" ,"document/role-and-workflow_product-maintenance.html")
+    
+    # Shared tools and documents
+    ,("shared/document/installation_Python.org" ,"shared/document/installation_Python.org")
+    ,("shared/document/installation_generic.org" ,"shared/document/installation_generic.org")
+    ,("shared/dictionary_style-directory.js" ,"shared/dictionary_style-directory.js")
+    ,("shared/tool/RTfmt" ,"shared/tool/RT-formatter")
+    ,("shared/tool/RT-formatter.el" ,"shared/tool/RT-formatter.el")
+    ,("shared/tool/makefile/target_kernel-module.mk" ,"shared/tool/makefile/target_kernel-module.mk")
+    
+    # Tester files (referenced by the old directory name before it is renamed)
+    ,("tester/RT-formatter/RT-formatter.el" ,"tester/RT-formatter/RT-formatter.el")
+    ,("tester/RT-formatter/RT-formatter.el" ,"tester/RT-formatter/RT-formatter_alt.el")
+    ,("tester/RT-formatter/RTfmt" ,"tester/RT-formatter/RT-formatter")
+    ,("tester/RT-formatter/RT-formatter.el" ,"tester/RT-formatter/RT-formatter_script.el")
+    ,("tester/RT-formatter/RTfmt_with_compare" ,"tester/RT-formatter/RT-formatter_with-compare")
+    ,("tester/RT-formatter/RTfmt_with_compare.el" ,"tester/RT-formatter/RT-formatter_with-compare.el")
+    ,("tester/RT-formatter/data_test-0.c" ,"tester/RT-formatter/data_test-0.c")
+    ,("tester/RT-formatter/data_test-1.py" ,"tester/RT-formatter/data_test-1.py")
+    
+    # Directories
+    ,("shared/linked-project" ,"shared/linked-project")
+    ,("tester/RT-formatter" ,"tester/RT-formatter")
+  ]
+
+  for src ,dst in substitutions:
+    if not os.path.exists(src):
+      print(f"Skipping (not found): {src}")
+      continue
+    
+    if os.path.exists(dst):
+      print(f"Warning: Destination {dst} already exists. Skipping rename for {src}.")
+      continue
+      
+    try:
+      os.rename(src ,dst)
+      print(f"Renamed: {src} -> {dst}")
+    except Exception as e:
+      print(f"Error renaming {src} to {dst}: {e}")
+
+  return 0
+
+if __name__ == "__main__":
+  sys.exit(CLI())
+
diff --git a/setup b/setup
index 2072dfd..7603f3e 100644 (file)
--- a/setup
+++ b/setup
@@ -36,18 +36,9 @@ fi
 
 # setup the project
 #
-  source shared/authored/setup
-
-# setup tools
-#
-  export PYTHON_HOME="${REPO_HOME}/shared/third_party/Python"
-  if [[ ":${PATH}:" != *":${PYTHON_HOME}/bin:"* ]]; then
-    export PATH="${PYTHON_HOME}/bin:${PATH}"
-  fi
-
-  RT_gcc="${REPO_HOME}/shared/third_party/RT_gcc/release"
-  if [[ ":${PATH}:" != *":${RT_gcc}:"* ]]; then
-    export PATH="${RT_gcc}:${PATH}"
+  source shared/tool/setup
+  if [[ -f "shared/authored/setup" ]]; then
+    source shared/authored/setup
   fi
 
 # setup the role
diff --git a/shared/authored/.gitkeep b/shared/authored/.gitkeep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/shared/authored/scratchpad b/shared/authored/scratchpad
deleted file mode 100755 (executable)
index f14f140..0000000
+++ /dev/null
@@ -1,225 +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, subprocess
-
-HELP = """usage: scratchpad {ls|clear|help|make|write|size|find|lock|unlock} [ARGS]
-  ls               List scratchpad in an indented tree with perms and owner (quiet if missing).
-  clear            Remove all contents of scratchpad/ except top-level .gitignore.
-  clear NAME       Remove scratchpad/NAME only.
-  make [NAME]      Ensure scratchpad/ exists with .gitignore; with NAME, mkdir scratchpad/NAME.
-  write SRC [DST]  Copy file/dir SRC into scratchpad (to DST if given; parents created).
-  size             Print 'empty' if only .gitignore; else total bytes and item count.
-  find [OPTS...]   Run system 'find' rooted at scratchpad/ with OPTS (omit literal 'scratchpad').
-  lock PATH...     Attempt 'chattr +i' on given paths under scratchpad/ (no state kept).
-  unlock PATH...   Attempt 'chattr -i' on given paths under scratchpad/.
-
-Examples:
-  scratchpad make
-  scratchpad write ~/Downloads/test.tar.gz
-  scratchpad find -type f -mtime +30 -print   # files older than 30 days
-  scratchpad lock some/dir important.txt
-  scratchpad unlock some/dir important.txt
-"""
-
-CWD = os.getcwd()
-SP = os.path.join(CWD, "scratchpad")
-GITIGNORE = os.path.join(SP, ".gitignore")
-
-def have_sp() -> bool:
-  return os.path.isdir(SP)
-
-def ensure_sp():
-  os.makedirs(SP, exist_ok=True)
-  ensure_gitignore()
-
-def ensure_gitignore():
-  os.makedirs(SP, exist_ok=True)
-  if not os.path.isfile(GITIGNORE):
-    with open(GITIGNORE, "w", encoding="utf-8") as f:
-      f.write("*\n!.gitignore\n")
-
-def filemode(mode: int) -> str:
-  try:
-    return stat.filemode(mode)
-  except Exception:
-    return oct(mode & 0o777)
-
-def owner_group(st) -> str:
-  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 rel_depth(base: str, root: str) -> int:
-  rel = os.path.relpath(base, root)
-  return 0 if rel == "." else rel.count(os.sep) + 1
-
-def ls_tree(root: str) -> None:
-  if not have_sp():
-    return
-  print("scratchpad/")
-
-  def walk(path: str, indent: str, is_root: bool) -> None:
-    try:
-      it = list(os.scandir(path))
-    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:
-      # 1) root-level hidden files first
-      for f in (e for e in files if e.name.startswith(".")):
-        st = os.lstat(f.path)
-        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{f.name}")
-      # 2) then directories (and recurse so children sit under the parent)
-      for d in dirs:
-        st = os.lstat(d.path)
-        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{d.name}/")
-        walk(d.path, indent + '  ', False)
-      # 3) then non-hidden files
-      for f in (e for e in files if not e.name.startswith(".")):
-        st = os.lstat(f.path)
-        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{f.name}")
-    else:
-      # subdirs: keep previous order (dirs first, then files; dotfiles naturally sort first)
-      for d in dirs:
-        st = os.lstat(d.path)
-        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{d.name}/")
-        walk(d.path, indent + '  ', False)
-      for f in files:
-        st = os.lstat(f.path)
-        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{f.name}")
-
-  walk(root, "  ", True)
-
-
-def clear_all() -> None:
-  if not have_sp():
-    return
-  for name in os.listdir(SP):
-    p = os.path.join(SP, name)
-    if name == ".gitignore" and os.path.isfile(p):
-      continue  # preserve only top-level .gitignore
-    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 clear_subdir(sub: str) -> None:
-  if not have_sp():
-    return
-  target = os.path.normpath(os.path.join(SP, sub))
-  try:
-    if os.path.commonpath([SP]) != os.path.commonpath([SP, target]):
-      return
-  except Exception:
-    return
-  if os.path.isdir(target) and not os.path.islink(target):
-    shutil.rmtree(target, ignore_errors=True)
-
-def cmd_make(args):
-  ensure_sp()
-  if args:
-    os.makedirs(os.path.join(SP, args[0]), exist_ok=True)
-
-def cmd_write(args):
-  if len(args) < 1:
-    print(HELP); return
-  if not have_sp():
-    ensure_sp()
-  src = args[0]
-  dst = args[1] if len(args) >= 2 else (os.path.basename(src.rstrip(os.sep)) or "untitled")
-  dst_path = os.path.normpath(os.path.join(SP, dst))
-  try:
-    if os.path.commonpath([SP]) != os.path.commonpath([SP, dst_path]):
-      print("refusing to write outside scratchpad", file=sys.stderr); return
-  except Exception:
-    print("invalid destination", file=sys.stderr); return
-  os.makedirs(os.path.dirname(dst_path), exist_ok=True)
-  if os.path.isdir(src):
-    if os.path.exists(dst_path):
-      shutil.rmtree(dst_path, ignore_errors=True)
-    shutil.copytree(src, dst_path, dirs_exist_ok=False)
-  else:
-    shutil.copy2(src, dst_path)
-
-def cmd_size():
-  if not have_sp():
-    return
-  names = os.listdir(SP)
-  if [n for n in names if n != ".gitignore"] == []:
-    print("empty"); return
-  total = 0; count = 0
-  for base, dirs, files in os.walk(SP):
-    for fn in files:
-      if fn == ".gitignore": 
-        continue
-      p = os.path.join(base, fn)
-      try:
-        total += os.path.getsize(p); count += 1
-      except OSError:
-        pass
-  print(f"bytes={total} items={count}")
-
-def cmd_find(args):
-  if not have_sp():
-    return
-  try:
-    subprocess.run(["find", SP] + args, check=False)
-  except FileNotFoundError:
-    print("find not available", file=sys.stderr)
-
-def cmd_chattr(flag: str, paths):
-  if not have_sp() or not paths:
-    return
-  try:
-    subprocess.run(["chattr", "-V"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
-  except FileNotFoundError:
-    print("chattr not available; lock/unlock skipped", file=sys.stderr); return
-  for rel in paths:
-    target = os.path.normpath(os.path.join(SP, rel))
-    try:
-      if os.path.commonpath([SP]) != os.path.commonpath([SP, target]):
-        continue
-    except Exception:
-      continue
-    try:
-      subprocess.run(["chattr", flag, target], check=False)
-    except Exception:
-      pass
-
-def CLI():
-  if len(sys.argv) < 2:
-    print(HELP); return
-  cmd, *args = sys.argv[1:]
-  if cmd == "ls":
-    if have_sp(): ls_tree(SP)
-    else: return
-  elif cmd == "clear":
-    if len(args) >= 1: clear_subdir(args[0])
-    else: clear_all()
-  elif cmd == "help":
-    print(HELP)
-  elif cmd == "make":
-    cmd_make(args)
-  elif cmd == "write":
-    cmd_write(args)
-  elif cmd == "size":
-    cmd_size()
-  elif cmd == "find":
-    cmd_find(args)
-  elif cmd == "lock":
-    cmd_chattr("+i", args)
-  elif cmd == "unlock":
-    cmd_chattr("-i", args)
-  else:
-    print(HELP)
-
-if __name__ == "__main__":
-  CLI()
diff --git a/shared/authored/setup b/shared/authored/setup
deleted file mode 100644 (file)
index 2284ba2..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/usr/bin/env bash
-script_afp=$(realpath "${BASH_SOURCE[0]}")
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
-  echo "$script_afp:: This script must be sourced, not executed."
-  exit 1
-fi
-
-# without this bash takes non-matching globs literally
-shopt -s nullglob
-
-# does not presume sharing or world permissions
-umask 0077
-
-# --------------------------------------------------------------------------------
-# project definition
-
-# actual absolute director path for this script file
-
-  script_adp(){
-    dirname "$script_afp"
-  }
-
-# assume this script is located  $REPO_HOME/tools_shared/authored and work backwards
-# to get $REPO_HOME, etc.
-
-  REPO_HOME=$(dirname "$(dirname "$(script_adp)")")
-  echo REPO_HOME "$REPO_HOME"
-
-  PROJECT=$(basename "$REPO_HOME")
-  echo PROJECT "$PROJECT"
-
-  # set the prompt decoration to the name of the project
-  PROMPT_DECOR=$PROJECT
-
-  export REPO_HOME PROJECT PROMPT_DECOR
-
-# --------------------------------------------------------------------------------
-# Project wide Tool setup
-#
-
-export VIRTUAL_ENV="$REPO_HOME/shared/third_party/Python"
-export PYTHON_HOME="$VIRTUAL_ENV"
-unset PYTHONHOME
-
-
-# --------------------------------------------------------------------------------
-# PATH
-#   precedence: last defined, first discovered
-  
-  PATH="$REPO_HOME/shared/third_party/RT-project-share/release/bash:$PATH"
-  PATH="$REPO_HOME/shared/third_party/RT-project-share/release/amd64:$PATH"
-  PATH="$REPO_HOME/shared/third_party:$PATH"
-  PATH="$REPO_HOME/shared/authored:$PATH"
-  PATH="$REPO_HOME/shared/made:$PATH"
-
-  # Remove duplicates
-  clean_path() {
-    PATH=$(echo ":$PATH" | awk -v RS=: -v ORS=: '!seen[$0]++' | sed 's/^://; s/:$//')
-  }
-  clean_path
-  export PATH
-
-# --------------------------------------------------------------------------------
-# the following functions are provided for other scripts to use.
-# at the top of files that make use of these functions put the following line:
-#  script_afp=$(realpath "${BASH_SOURCE[0]}")
-#
-
-  ## script's filename
-  script_fn(){
-    basename "$script_afp"
-  }
-
-  ## script's dirpath relative to $REPO_HOME
-  script_fp(){
-    realpath --relative-to="${REPO_HOME}" "$script_afp"
-  }
-
-  ## script's dirpath relative to $REPO_HOME
-  script_dp(){
-    dirname "$(script_fp)"
-  }
-
-  export -f script_adp script_fn script_dp script_fp
-
-#--------------------------------------------------------------------------------
-# used by release scripts
-#
-
-  install_file() {
-    if [ "$#" -lt 3 ]; then
-      echo "env::install_file usage: install_file <source1> <source2> ... <target_dir> <permissions>"
-      return 1
-    fi
-
-    perms="${@: -1}"         # Last argument is permissions
-    target_dp="${@: -2:1}"   # Second-to-last argument is the target directory
-    sources=("${@:1:$#-2}")  # All other arguments are source files
-
-    if [ ! -d "$target_dp" ]; then
-      echo "env::install_file no install done: target directory '$target_dp' does not exist."
-      return 1
-    fi
-
-    for source_fp in "${sources[@]}"; do
-      if [ ! -f "$source_fp" ]; then
-        echo "env::install_file: source file '$source_fp' does not exist."
-        return 1
-      fi
-
-      target_file="$target_dp/$(basename "$source_fp")"
-
-      if ! install -m "$perms" "$source_fp" "$target_file"; then
-        echo "env::install_file: Failed to install $(basename "$source_fp") to $target_dp"
-        return 1
-      else
-        echo "env::install_file: installed $(basename "$source_fp") to $target_dp with permissions $perms"
-      fi
-    done
-  }
-
-  export -f install_file
-
-# --------------------------------------------------------------------------------
-# closing
-#
-  if [[ -z "$ENV" ]]; then
-    export ENV=$(script_fp)
-  fi
-
diff --git a/shared/authored/style/.gitkeep b/shared/authored/style/.gitkeep
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/shared/authored/version b/shared/authored/version
deleted file mode 100755 (executable)
index 45626d4..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-echo "Harmony v2.2 2026-03-06"
-echo "RT-style-JS_public v3.1 2026-06-20 10:39:27 Z"
-
-
-
diff --git a/shared/document/install_Python.org b/shared/document/install_Python.org
deleted file mode 100644 (file)
index 14342f9..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#+TITLE: Installing Python in Harmony
-#+AUTHOR: Thomas Walker Lynch
-#+OPTIONS: toc:2 num:nil
-
-* Overview
-
-This document describes how to install a project-local Python environment under:
-
-#+begin_src bash
-shared/third_party/Python
-#+end_src
-
-This environment is shared across the =developer= and =tester= roles and is automatically activated through their respective =env_<role>= scripts.
-
-* Precondition
-
-Ensure the following:
-
-- You are in a POSIX shell with =python3= installed.
-- The =python3-venv= package is available (on Debian: =sudo apt install python3-venv=).
-- You have sourced the Harmony environment via =env_toolsmith= to initialize =REPO_HOME= and related variables.
-
-* Step-by-Step Installation
-
-1. Source the Harmony environment:
-   #+begin_src bash
-   source env_toolsmith
-   #+end_src
-
-2. Create the virtual environment:
-   #+begin_src bash
-   python3 -m venv "$REPO_HOME/shared/third_party/Python"
-   #+end_src
-
-3. Activate it temporarily to install required packages:
-   #+begin_src bash
-   source "$REPO_HOME/shared/third_party/Python/bin/activate"
-   pip install --upgrade pip
-   pip install pytest  # Add any shared packages here
-   deactivate
-   #+end_src
-
-4. Rename Python's default activate and deactivate:
-   Harmony provides its own role-aware environment management. Using Python’s default activation scripts may interfere with prompt logic, PATH order, and role-specific behavior.
-
-   Disable the default scripts by renaming them:
-   #+begin_src bash
-   mv "$REPO_HOME/shared/third_party/Python/bin/activate" \
-      "$REPO_HOME/shared/third_party/Python/bin/activate_deprecated"
-   #+end_src
-
-   This ensures that accidental sourcing of Python’s =activate= script won't override Harmony's environment setup.
-
-5. Verify installation:
-   #+begin_src bash
-   ls "$REPO_HOME/shared/third_party/Python/bin/python3"
-   #+end_src
-
-   The binary should exist and report a working Python interpreter when run.
-
-* Notes
-
-- The virtual environment is deliberately named =Python=, not =venv=, to reflect its role as a shared system component.
-- Harmony environment scripts define and control =VIRTUAL_ENV=, =PYTHON_HOME=, and =PATH=, making Python activation seamless and uniform.
-- There is no need to use Python’s =bin/activate= directly — it is fully replaced by Harmony’s environment logic.
-
-* Related Files
-
-- =shared/authored/env=
-- =shared/authored/env_source=
-- =env_developer=, =env_tester=, =env_toolsmith=
-
-* Last Verified
-
-2025-05-19 :: Activate/deactivate renamed post-install. Requires Harmony environment sourcing prior to execution.
diff --git a/shared/document/install_generic.org b/shared/document/install_generic.org
deleted file mode 100644 (file)
index 90af13e..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-
-This is the generic install.org doc that comes with the skeleton.
-
-1. $REPO_HOME/shared/third_party/.gitignore:
-
-    * 
-    !/.gitignore
-    !/patch
-
-  The only things from the third party directory that will be pushed to the repo origin is the .gitignore file and the patches.
-
-
-2. downloaded tar files etc. go into the directory `upstream`
-
-    $REPO_HOME/shared/upstream
-   Typically the contents of upstream are deleted after the install.
-
-3. for the base install
-
-     cd $REPO_HOME/shared/third_party
-     do whatever it takes to install tool, as examples:
-       git clone <tool_path>
-       tar -xzf ../upstream/tar
-       ...
-
-   Be sure to add the path to the tool executable(s) in the $REPO_HOME/env_$ROLE files for the $ROLE who uses the tool.
-
-   Assuming you are not also developing the tool, for safety
-   change each installed git project to a local branch:
-
-     b=<site>_<project>_local_$USER
-     git switch -c "$b"
-   
-
-4. Define some variables to simplify our discussion. Lowercase variable names
-   are not exported from the shell.
-
-     # already set in the environment
-     #   REPO_HOME
-     #   PROJECT
-     #   USER
-
-    # example tool names: 'RT_gcc' 'RT-project share`  etc.
-     tool=<tool-name>  
-     tool_dpath="$REPO_HOME/shared/third_party/$tool"
-     patch_dpath="$REPO_HOME/shared/patch/"
-
-
-5. create a patch series (from current vendor state → your local edits)
-
-   # this can be repeated and will create an encompassing diff file
-
-   # optionally crate a new branch after cloning the third party tool repo and work from there.  You won't make any commits, but in case you plan to ever check the changes in, or have a the bad habit of doing ommits burned into your brain-stem, making a brnch will help.
-
-   # make changes
-
-     cd "$tool_dpath"
-
-     # do your edits
-
-     # Stage edits. Do not commit them!!  Be sure you are in the third party
-     # tool directory when doing `git add -A` and `git diff` commands.
-     git add -A
-
-     # diff the stage from the current repo to create the patch file
-     git diff --staged > "$patch_dpath/$tool"
-
-     # the diff file can be added to the project and checked in at the project level.
-
-
-6. how to apply an existing patch
-
-   Get a fresh clone of the tool into $tool_dpath.
-
-     cd "$tool_dpath"
-     git apply "$patch_dpath/$tool"
-
-   You can see what `git apply` would do by running 
-
-     git apply --check /path/to/your/patch_dpath/$tool
diff --git a/shared/document/installation_Python.org b/shared/document/installation_Python.org
new file mode 100644 (file)
index 0000000..06155b7
--- /dev/null
@@ -0,0 +1,73 @@
+#+TITLE: Installing Python in Harmony
+#+AUTHOR: Thomas Walker Lynch
+#+OPTIONS: toc:2 num:nil
+
+* Overview
+
+This document describes how to install a project-local Python environment under:
+
+#+begin_src bash
+shared/linked-project/Python
+#+end_src
+
+* Precondition
+
+Ensure the following:
+
+- You are in a POSIX shell with =python3= installed.
+- The =python3-venv= package is available (on Debian: =sudo apt install python3-venv=).
+- You have sourced the Harmony environment via =env_toolsmith= to initialize =REPO_HOME= and related variables.
+
+* Step-by-Step Installation
+
+1. Source the Harmony environment:
+   #+begin_src bash
+   source env_toolsmith
+   #+end_src
+
+2. Create the virtual environment:
+   #+begin_src bash
+   python3 -m venv "$REPO_HOME/shared/linked-project/Python"
+   #+end_src
+
+3. Activate it temporarily to install required packages:
+   #+begin_src bash
+   source "$REPO_HOME/shared/linked-project/Python/bin/activate"
+   pip install --upgrade pip
+   pip install pytest  # Add any shared packages here
+   deactivate
+   #+end_src
+
+4. Rename Python's default activate and deactivate:
+   Harmony provides its own role-aware environment management. Using Python’s default activation scripts may interfere with prompt logic, PATH order, and role-specific behavior.
+
+   Disable the default scripts by renaming them:
+   #+begin_src bash
+   mv "$REPO_HOME/shared/linked-project/Python/bin/activate" \
+      "$REPO_HOME/shared/linked-project/Python/bin/activate_deprecated"
+   #+end_src
+
+   This ensures that accidental sourcing of Python’s =activate= script won't override Harmony's environment setup.
+
+5. Verify installation:
+   #+begin_src bash
+   ls "$REPO_HOME/shared/linked-project/Python/bin/python3"
+   #+end_src
+
+   The binary should exist and report a working Python interpreter when run.
+
+* Notes
+
+- The virtual environment is deliberately named =Python=, not =venv=, to reflect its role as a shared system component.
+- Harmony environment scripts define and control =VIRTUAL_ENV=, =PYTHON_HOME=, and =PATH=, making Python activation seamless and uniform.
+- There is no need to use Python’s =bin/activate= directly — it is fully replaced by Harmony’s environment logic.
+
+* Related Files
+
+- =shared/authored/env=
+- =shared/authored/env_source=
+- =env_developer=, =env_tester=, =env_toolsmith=
+
+* Last Verified
+
+2025-05-19 :: Activate/deactivate renamed post-install. Requires Harmony environment sourcing prior to execution.
diff --git a/shared/document/installation_generic.org b/shared/document/installation_generic.org
new file mode 100644 (file)
index 0000000..db88651
--- /dev/null
@@ -0,0 +1,81 @@
+
+This is the generic install.org doc that comes with the skeleton.
+
+1. $REPO_HOME/shared/linked-project/.gitignore:
+
+    * 
+    !/.gitignore
+    !/patch
+
+  The only things from the third party directory that will be pushed to the repo origin is the .gitignore file and the patches.
+
+
+2. downloaded tar files etc. go into the directory `upstream`
+
+    $REPO_HOME/shared/upstream
+   Typically the contents of upstream are deleted after the install.
+
+3. for the base install
+
+     cd $REPO_HOME/shared/linked-project
+     do whatever it takes to install tool, as examples:
+       git clone <tool_path>
+       tar -xzf ../upstream/tar
+       ...
+
+   Be sure to add the path to the tool executable(s) in the $REPO_HOME/env_$ROLE files for the $ROLE who uses the tool.
+
+   Assuming you are not also developing the tool, for safety
+   change each installed git project to a local branch:
+
+     b=<site>_<project>_local_$USER
+     git switch -c "$b"
+   
+
+4. Define some variables to simplify our discussion. Lowercase variable names
+   are not exported from the shell.
+
+     # already set in the environment
+     #   REPO_HOME
+     #   PROJECT
+     #   USER
+
+    # example tool names: 'RT_gcc' 'RT-project share`  etc.
+     tool=<tool-name>  
+     tool_dpath="$REPO_HOME/shared/linked-project/$tool"
+     patch_dpath="$REPO_HOME/shared/patch/"
+
+
+5. create a patch series (from current vendor state → your local edits)
+
+   # this can be repeated and will create an encompassing diff file
+
+   # optionally crate a new branch after cloning the third party tool repo and work from there.  You won't make any commits, but in case you plan to ever check the changes in, or have a the bad habit of doing ommits burned into your brain-stem, making a brnch will help.
+
+   # make changes
+
+     cd "$tool_dpath"
+
+     # do your edits
+
+     # Stage edits. Do not commit them!!  Be sure you are in the third party
+     # tool directory when doing `git add -A` and `git diff` commands.
+     git add -A
+
+     # diff the stage from the current repo to create the patch file
+     git diff --staged > "$patch_dpath/$tool"
+
+     # the diff file can be added to the project and checked in at the project level.
+
+
+6. how to apply an existing patch
+
+   Get a fresh clone of the tool into $tool_dpath.
+
+     cd "$tool_dpath"
+     git apply "$patch_dpath/$tool"
+
+   You can see what `git apply` would do by running 
+
+     git apply --check /path/to/your/patch_dpath/$tool
index 77ec522..de1173d 100644 (file)
@@ -1,4 +1,4 @@
 window.RT_REPO_ROOT = "../../";
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/style_directory_dict.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/loader.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/body_visibility_hidden.js"></script>');
+document.write('<script src="' + window.RT_REPO_ROOT + 'shared/dictionary_style-directory.js"></script>');
+document.write('<script src="' + window.RT_REPO_ROOT + 'shared/linked-project/RT-style-JS_public/consumer/release/RT/core/loader.js"></script>');
+document.write('<script src="' + window.RT_REPO_ROOT + 'shared/linked-project/RT-style-JS_public/consumer/release/RT/core/body_visibility_hidden.js"></script>');
diff --git a/shared/style_directory_dict.js b/shared/style_directory_dict.js
deleted file mode 100644 (file)
index 5c08723..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-window.StyleRT_namespaces = {
-  "RT": window.RT_REPO_ROOT + "developer/authored/RT"
-  ,"Project": window.RT_REPO_ROOT + "shared/authored/style"
-};
diff --git a/shared/tool/PATH.sh b/shared/tool/PATH.sh
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/shared/tool/RT-formatter b/shared/tool/RT-formatter
new file mode 100644 (file)
index 0000000..0e57d5a
--- /dev/null
@@ -0,0 +1,326 @@
+#!/usr/bin/env -S python3 -B
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+RTfmt — Reasoning Technology code formatter (Predicate Tokenizer)
+
+Commands:
+  RTfmt write [--lisp] <file ...>      Format files in place (rewrite originals)
+  RTfmt copy  [--lisp] <file ...>      Save backups as <file>~ then format originals
+  RT-formatter pipe  [--lisp]                 Read from stdin, write to stdout
+  RTfmt self_test                      Run built-in tests
+  RTfmt version                        Show tool version
+  RTfmt help | --help                  Show usage
+"""
+
+import sys ,re ,shutil ,os
+from typing import List ,Tuple ,Optional ,TextIO
+
+RTF_VERSION = "0.5.0-predicate"
+
+def get_usage() -> str:
+  prog_name = os.path.basename(sys.argv[0])
+  return f"""\
+Usage:
+  {prog_name} write [--lisp] <file ...>
+  {prog_name} copy  [--lisp] <file ...>
+  {prog_name} pipe  [--lisp]
+  {prog_name} self_test
+  {prog_name} version
+  {prog_name} help | --help
+"""
+
+# Removed < and > so they are treated as standard CODE operators
+BR_OPEN  = "([{"
+BR_CLOSE = ")]}"
+PAIR = dict( zip(BR_OPEN ,BR_CLOSE) )
+REV  = dict( zip(BR_CLOSE ,BR_OPEN) )
+
+# --------------- Lexer ----------------
+
+class RT_Token:
+  def __init__(self ,kind: str ,text: str):
+    self.kind = kind
+    self.text = text
+
+  def __repr__(self):
+    return f"<{self.kind}:{repr(self.text)}>"
+
+TOKEN_REGEX = re.compile(
+  r'(?P<COMMENT>//[^\n]*|#[^\n]*|(?s:/\*.*?\*/))'
+  r'|(?P<STRING>"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')'
+  r'|(?P<SPACE>[ \t]+)'
+  r'|(?P<NEWLINE>\n)'
+  r'|(?P<COMMA>,)'
+  r'|(?P<BR_OPEN>[\[\(\{])'
+  r'|(?P<BR_CLOSE>[\]\)\}])'
+  r'|(?P<CODE>[^ \t\n,\[\(\{\]\)\}"\'#/]+|/)'
+)
+
+def tokenize(text: str) -> List[RT_Token]:
+  tokens = []
+  for TM_match in TOKEN_REGEX.finditer(text):
+    kind = TM_match.lastgroup
+    text_val = TM_match.group(kind)
+    tokens.append( RT_Token(kind ,text_val) )
+  return tokens
+
+# --------------- Intelligence API ----------------
+
+class TokenStream:
+  def __init__(self ,tokens: List[RT_Token]):
+    self.tokens = tokens
+
+  def get_token(self ,index: int) -> Optional[RT_Token]:
+    if 0 <= index < len(self.tokens):
+      return self.tokens[index]
+    return None
+
+  def next_sig_index(self ,index: int) -> Optional[int]:
+    for TM_i in range(index + 1 ,len(self.tokens)):
+      if self.tokens[TM_i].kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
+        return TM_i
+    return None
+
+  def is_first_on_line(self ,index: int) -> bool:
+    for TM_i in range(index - 1 ,-1 ,-1):
+      k = self.tokens[TM_i].kind
+      if k == "NEWLINE":
+        return True
+      if k != "SPACE":
+        return False
+    return True # Start of file
+
+  def indent_of_line(self ,index: int) -> str:
+    for TM_i in range(index ,-1 ,-1):
+      if self.tokens[TM_i].kind == "NEWLINE":
+        if TM_i + 1 < len(self.tokens) and self.tokens[TM_i + 1].kind == "SPACE":
+          return self.tokens[TM_i + 1].text
+        return ""
+    if self.tokens and self.tokens[0].kind == "SPACE":
+      return self.tokens[0].text
+    return ""
+
+  def indent_of_left_match(self ,index: int) -> Optional[str]:
+    tok = self.get_token(index)
+    if not tok or tok.kind != "BR_CLOSE":
+      return None
+    target_opener = REV[tok.text]
+    depth = 0
+    for TM_i in range(index - 1 ,-1 ,-1):
+      t = self.tokens[TM_i]
+      if t.kind == "BR_CLOSE":
+        depth += 1
+      elif t.kind == "BR_OPEN":
+        if depth > 0:
+          depth -= 1
+        elif t.text == target_opener:
+          return self.indent_of_line(TM_i)
+    return None
+
+# --------------- Rule Engine ----------------
+
+def rule_migrate_vertical_commas(stream: TokenStream):
+  TM_i = 0
+  while TM_i < len(stream.tokens):
+    if stream.tokens[TM_i].kind == "COMMA":
+      is_trailing = False
+      next_sig = stream.next_sig_index(TM_i)
+      if next_sig is not None:
+        for TM_j in range(TM_i + 1 ,next_sig):
+          if stream.tokens[TM_j].kind == "NEWLINE":
+            is_trailing = True
+            break
+      
+      if is_trailing:
+        comma_tok = stream.tokens.pop(TM_i)
+        next_sig -= 1 # Shifted because of pop
+        stream.tokens.insert(next_sig ,comma_tok)
+        continue
+    TM_i += 1
+
+def rule_format_horizontal_commas(stream: TokenStream):
+  for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+    if stream.tokens[TM_i].kind == "COMMA":
+      if stream.is_first_on_line(TM_i):
+        continue
+      
+      next_tok = stream.get_token(TM_i + 1)
+      if next_tok and next_tok.kind == "SPACE":
+        stream.tokens.pop(TM_i + 1)
+      
+      prev_tok = stream.get_token(TM_i - 1)
+      if prev_tok and prev_tok.kind == "SPACE":
+        if prev_tok.text != " ":
+          prev_tok.text = " "
+      else:
+        stream.tokens.insert(TM_i ,RT_Token("SPACE" ," "))
+
+def rule_fix_closing_indent(stream: TokenStream):
+  for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+    if stream.tokens[TM_i].kind == "BR_CLOSE" and stream.is_first_on_line(TM_i):
+      target_indent = stream.indent_of_left_match(TM_i)
+      if target_indent is not None:
+        prev = stream.get_token(TM_i - 1)
+        if prev and prev.kind == "SPACE":
+          prev.text = target_indent
+        else:
+          stream.tokens.insert(TM_i ,RT_Token("SPACE" ,target_indent))
+
+def rule_tighten_brackets(stream: TokenStream):
+  for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+    if stream.tokens[TM_i].kind == "SPACE" and not stream.is_first_on_line(TM_i):
+      prev_t = stream.get_token(TM_i - 1)
+      next_t = stream.get_token(TM_i + 1)
+      if (prev_t and prev_t.kind == "BR_OPEN") or (next_t and next_t.kind == "BR_CLOSE"):
+        stream.tokens.pop(TM_i)
+
+def get_bracket_spans(stream: TokenStream) -> List[Tuple[int ,int]]:
+  stack = []
+  spans = []
+  for TM_i ,tok in enumerate(stream.tokens):
+    if tok.kind == "BR_OPEN":
+      stack.append( (tok.text ,TM_i) )
+    elif tok.kind == "BR_CLOSE":
+      if stack and REV[tok.text] == stack[-1][0]:
+        _ ,pos = stack.pop()
+        if not stack:
+          spans.append( (pos ,TM_i) )
+  return spans
+
+def rule_pad_outermost(stream: TokenStream ,is_lisp: bool):
+  if is_lisp:
+    return
+  while True:
+    spans = get_bracket_spans(stream)
+    changed = False
+    for TM_start ,TM_end in reversed(spans):
+      has_inner = False
+      for TM_k in range(TM_start + 1 ,TM_end):
+        if stream.tokens[TM_k].kind in ("BR_OPEN" ,"BR_CLOSE"):
+          has_inner = True
+          break
+      
+      if has_inner:
+        left_has = (TM_start + 1 < len(stream.tokens)) and stream.tokens[TM_start + 1].kind == "SPACE"
+        right_has = (TM_end - 1 >= 0) and stream.tokens[TM_end - 1].kind == "SPACE"
+        if not left_has or not right_has:
+          if not right_has:
+            stream.tokens.insert(TM_end ,RT_Token("SPACE" ," "))
+          if not left_has:
+            stream.tokens.insert(TM_start + 1 ,RT_Token("SPACE" ," "))
+          changed = True
+          break
+    if not changed:
+      break
+
+# --------------- Public API ----------------
+
+def format_tokens(tokens: List[RT_Token] ,is_lisp: bool) -> str:
+  stream = TokenStream(tokens)
+  
+  rule_migrate_vertical_commas(stream)
+  rule_format_horizontal_commas(stream)
+  rule_tighten_brackets(stream)
+  rule_fix_closing_indent(stream)
+  rule_pad_outermost(stream ,is_lisp)
+  
+  return "".join(t.text for t in stream.tokens)
+
+def rt_format_text(text: str ,is_lisp: bool) -> str:
+  tokens = tokenize(text)
+  return format_tokens(tokens ,is_lisp)
+
+def rt_format_stream(inp: TextIO ,out: TextIO ,is_lisp: bool) -> None:
+  text = inp.read()
+  out.write( rt_format_text(text ,is_lisp) )
+
+# --------------- Self-test ----------------
+
+def run_self_test() -> bool:
+  ok = True
+  def chk(src ,exp):
+    nonlocal ok
+    got = rt_format_text(src ,False)
+    if got != exp:
+      print("FAIL:\n" + src + "\n=>\n" + got + "\nexpected:\n" + exp)
+      ok = False
+
+  chk("a,b,c" ,"a ,b ,c")
+  chk("a , b ,  c" ,"a ,b ,c")
+  chk("  ,vertical_arg" ,"  ,vertical_arg") 
+
+  chk("int a=0,\n  b=1,\n  c=2;" ,"int a=0\n  ,b=1\n  ,c=2;") 
+
+  chk("f ( x )" ,"f(x)")
+  chk("f(x) + g(y)" ,"f(x) + g(y)")
+  chk("  {" ,"  {") 
+
+  src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
+  exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
+  chk(src ,exp)
+
+  chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
+  
+  # Operator protection check
+  chk("for(int TM = 0; TM < count; ++TM)" ,"for(int TM = 0; TM < count; ++TM)")
+
+  print("SELFTEST OK" if ok else "SELFTEST FAILED")
+  return ok
+
+# --------------- CLI ----------------
+def write_files(paths: List[str] ,is_lisp: bool) -> int:
+  for TM_path in paths:
+    with open(TM_path ,"r" ,encoding="utf-8") as f:
+      data = f.read()
+    formatted = rt_format_text(data ,is_lisp)
+    with open(TM_path ,"w" ,encoding="utf-8") as f:
+      f.write(formatted)
+  return 0
+
+def copy_files(paths: List[str] ,is_lisp: bool) -> int:
+  for TM_path in paths:
+    shutil.copy2(TM_path ,TM_path + "~")
+  return write_files(paths ,is_lisp)
+
+def CLI(argv=None) -> int:
+  args = list(sys.argv[1:] if argv is None else argv)
+  usage_text = get_usage()
+  
+  if not args or args[0] in {"help" ,"--help" ,"-h"}:
+    print(usage_text)
+    return 0
+
+  is_lisp = "--lisp" in args
+  args = [TM_a for TM_a in args if TM_a != "--lisp"]
+  
+  if not args:
+    return 0
+
+  cmd = args[0]
+  rest = args[1:]
+
+  if cmd == "version":
+    print(RTF_VERSION)
+    return 0
+  if cmd == "self_test":
+    ok = run_self_test()
+    return 0 if ok else 1
+  if cmd == "pipe":
+    rt_format_stream(sys.stdin ,sys.stdout ,is_lisp)
+    return 0
+  if cmd == "write":
+    if not rest:
+      print("write: missing <file ...>\n" + usage_text)
+      return 2
+    return write_files(rest ,is_lisp)
+  if cmd == "copy":
+    if not rest:
+      print("copy: missing <file ...>\n" + usage_text)
+      return 2
+    return copy_files(rest ,is_lisp)
+
+  print(f"Unknown command: {cmd}\n" + usage_text)
+  return 2
+
+if __name__ == "__main__":
+  sys.exit( CLI() )
diff --git a/shared/tool/RT-formatter.el b/shared/tool/RT-formatter.el
new file mode 100644 (file)
index 0000000..c0e2c40
--- /dev/null
@@ -0,0 +1,22 @@
+(defun RT-formatter-buffer ()
+  "Format the current buffer using RTfmt0."
+  (interactive)
+  (if (not (executable-find "RT-formatter"))
+      (message "Error: RTfmt0 executable not found in PATH.")
+    (let ((temp-buffer (generate-new-buffer " *RTfmt0*"))
+          (args (list "pipe")))
+      (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
+        (setq args (append args (list "--lisp"))))
+      (unwind-protect
+          (let ((exit-code (apply #'call-process-region
+                                  (point-min) (point-max)
+                                  "RT-formatter"
+                                  nil temp-buffer nil
+                                  args)))
+            (if (zerop exit-code)
+                (progn
+                  ;; Applies a non-destructive diff, preserving point and markers natively
+                  (replace-buffer-contents temp-buffer)
+                  (message "RT-formatter formatting successful."))
+              (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
+        (kill-buffer temp-buffer)))))
diff --git a/shared/tool/env_to_emacs b/shared/tool/env_to_emacs
new file mode 100755 (executable)
index 0000000..947efc0
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+
+set -euo pipefail
+
+if [ -z "${REPO_HOME:-}" ]; then
+  echo "emacs-sync:: error: REPO_HOME is not set. Source a setup environment first."
+  exit 1
+fi
+
+if ! command -v emacsclient >/dev/null 2>&1; then
+  echo "emacs-sync:: error: emacsclient is not installed or not in PATH."
+  exit 1
+fi
+
+# Push variables to the Emacs global environment
+emacsclient --eval "(setenv \"REPO_HOME\" \"$REPO_HOME\")" > /dev/null
+emacsclient --eval "(setenv \"PROJECT\" \"$PROJECT\")" > /dev/null
+
+if [ -n "${ROLE:-}" ]; then
+  emacsclient --eval "(setenv \"ROLE\" \"$ROLE\")" > /dev/null
+fi
+
+# Sync the PATH so Emacs subprocesses find the local tools
+emacsclient --eval "(setenv \"PATH\" \"$PATH\")" > /dev/null
+
+# Update exec-path for native Emacs executable resolution
+emacsclient --eval "(setq exec-path (append (parse-colon-path \"$PATH\") (list exec-directory)))" > /dev/null
+
+echo "Emacs environment synchronized for project: $PROJECT"
diff --git a/shared/tool/link-RT b/shared/tool/link-RT
new file mode 100755 (executable)
index 0000000..0e15005
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+import os
+import sys
+import shutil
+from pathlib import Path
+
+def run():
+    repo_home_env = os.environ.get('REPO_HOME')
+    if not repo_home_env:
+        print("Error: REPO_HOME environment variable is missing.", file=sys.stderr)
+        sys.exit(1)
+
+    repo_home = Path(repo_home_env)
+    
+    # Define the canonical target location
+    target_src = repo_home / 'shared' / 'linked-project' / 'RT-style-JS_public' / 'consumer' / 'release' / 'RT'
+
+    # Determine the destination directory
+    if len(sys.argv) > 1:
+        dest_dir = Path(sys.argv[1]).resolve()
+    else:
+        dest_dir = Path.cwd()
+
+    if not dest_dir.is_dir():
+        print(f"Error: Directory '{dest_dir}' does not exist.", file=sys.stderr)
+        sys.exit(1)
+
+    link_dest = dest_dir / 'RT'
+
+    # Clobber existing file, link, or directory named 'RT'
+    if link_dest.is_symlink() or link_dest.is_file():
+        link_dest.unlink()
+    elif link_dest.is_dir():
+        shutil.rmtree(link_dest)
+
+    # Establish the new symbolic link
+    try:
+        link_dest.symlink_to(target_src)
+        print(f"Established link: {link_dest} -> {target_src}")
+    except OSError as e:
+        print(f"Failed to establish link: {e}", file=sys.stderr)
+        sys.exit(1)
+
+if __name__ == '__main__':
+    run()
\ No newline at end of file
diff --git a/shared/tool/makefile/Harmony.mk b/shared/tool/makefile/Harmony.mk
new file mode 100644 (file)
index 0000000..eaf1ce8
--- /dev/null
@@ -0,0 +1,135 @@
+# make/Harmony.mk - build *.lib.c and *.CLI.c
+# written for the Harmony skeleton, always invoked from cwd  $REPO_HOME/<role>
+# files have two suffixes by convention, e.g.: X.lib.c or Y.CLI.c 
+
+.SUFFIXES:
+.DELETE_ON_ERROR:
+
+#--------------------------------------------------------------------------------
+# Harmony structure
+
+SHELL=/bin/bash
+
+ECHO := printf "%b\n"
+
+C_SOURCE_DIR     ?= authored
+C                ?= gcc
+CFLAGS           ?= -std=gnu11 -Wall -Wextra -Wpedantic -finput-charset=UTF-8
+CFLAGS           += -MMD -MP
+CFLAGS           += -include "$(REPO_HOME)/shared/tool/makefile/RT_global.h"
+CFLAGS           += -I $(C_SOURCE_DIR)
+
+# Project administrators can override this in their local makefile
+LIBRARY_NAME     ?= $(PROJECT)
+LIBRARY_NAME     := $(subst -,_,$(LIBRARY_NAME))
+
+BUILD_DIR        ?= scratchpad/build
+OBJECT_DIR       ?= $(BUILD_DIR)/object
+LIBRARY_DIR      ?= scratchpad/made
+MACHINE_DIR      ?= scratchpad/made
+
+LIBRARY_FILE     ?= $(LIBRARY_DIR)/lib$(LIBRARY_NAME).a
+
+LN_FLAGS         ?= -L$(LIBRARY_DIR) -L/lib64 -L/lib -l$(LIBRARY_NAME)
+
+KMOD_SOURCE_DIR  ?= authored
+KMOD_CCFLAGS     ?= -I $(KMOD_SOURCE_DIR)
+# Pass the global header to Kbuild exactly as done for user-space
+KMOD_CCFLAGS     += -include $(REPO_HOME)/shared/tool/makefile/RT_global.h
+KMOD_OUTPUT_DIR  ?= scratchpad/kmod
+
+#--------------------------------------------------------------------------------
+# derived variables
+
+# source discovery (single dir)
+c_source_lib  := $(wildcard $(C_SOURCE_DIR)/*.lib.c)
+c_source_exec := $(wildcard $(C_SOURCE_DIR)/*.CLI.c)
+
+# remove suffix to get base name
+c_base_lib  := $(sort $(patsubst %.lib.c,%, $(notdir $(c_source_lib))))
+c_base_exec := $(sort $(patsubst %.CLI.c,%, $(notdir $(c_source_exec))))
+
+# two sets of object files, one for the lib, and one for the CLI programs
+object_lib  := $(patsubst %, $(OBJECT_DIR)/%.lib.o, $(c_base_lib))
+object_exec := $(patsubst %, $(OBJECT_DIR)/%.CLI.o, $(c_base_exec))
+
+# executables are made from exec_ sources
+exec_ := $(patsubst %, $(MACHINE_DIR)/%, $(c_base_exec))
+
+#--------------------------------------------------------------------------------
+# pull in dependencies
+
+-include $(object_lib:.o=.d) $(object_exec:.o=.d)
+
+
+#--------------------------------------------------------------------------------
+# targets
+
+# when no target is given make uses the first target, this one
+.PHONY: usage
+usage:
+       @echo example usage: make clean
+       @echo example usage: make library
+       @echo example usage: make CLI
+       @echo example usage: make library CLI
+
+.PHONY: version
+version:
+       @echo makefile version 8.0
+       if [ ! -z "$(C)" ]; then $(C) -v; fi
+       /bin/make -v
+
+.PHONY: information
+information:
+       @printf "· → Unicode middle dot - visible: [%b]\n" "·"
+       @echo "C_SOURCE_DIR: " $(C_SOURCE_DIR)
+       @echo "BUILD_DIR: " $(BUILD_DIR)
+       @echo "c_source_lib: " $(c_source_lib)
+       @echo "c_source_exec: " $(c_source_exec)
+       @echo "c_base_lib: " $(c_base_lib)
+       @echo "c_base_exec: " $(c_base_exec)
+       @echo "object_lib: " $(object_lib)
+       @echo "object_exec: " $(object_exec)
+       @echo "exec_: " $(exec_)
+
+.PHONY: library
+library: $(LIBRARY_FILE)
+
+$(LIBRARY_FILE): $(object_lib)
+       @mkdir -p $(MACHINE_DIR)
+       @if [ -s "$@" ] || [ -n "$(object_lib)" ]; then \
+               echo "ar rcs $@ $^"; \
+               ar rcs $@ $^; \
+       else \
+               rm -f "$@"; \
+       fi   
+
+#.PHONY: CLI
+#CLI: $(LIBRARY_FILE) $(exec_)
+
+.PHONY: CLI
+CLI: library $(exec_)
+
+
+# generally better to use the project local clean scripts, but this will make it so that the make targets can be run again
+
+.PHONY: clean
+clean:
+       rm -f $(LIBRARY_FILE)
+       for obj in $(object_lib) $(object_exec); do rm -f $$obj $${obj%.o}.d || true; done
+       for i in $(exec_); do [ -e $$i ] && rm $$i || true; done
+       rm -rf $(BUILD_DIR)
+
+
+# recipes
+#$(OBJECT_DIR)/%.o: $(C_SOURCE_DIR)/%.c
+#      @mkdir -p $(OBJECT_DIR)
+#      $(C) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
+
+$(OBJECT_DIR)/%.o: $(C_SOURCE_DIR)/%.c
+       @mkdir -p $(OBJECT_DIR)
+       $(C) $(CFLAGS) $(if $(findstring .lib,$@),-D$(if $(NAMESPACE),$(NAMESPACE)·,)$(basename $*)) -o $@ -c $<
+
+$(MACHINE_DIR)/%: $(OBJECT_DIR)/%.CLI.o
+       @mkdir -p $(MACHINE_DIR)
+       $(C) -o $@ $< $(LN_FLAGS)
diff --git a/shared/tool/makefile/RT_global.h b/shared/tool/makefile/RT_global.h
new file mode 100644 (file)
index 0000000..29f2e63
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef RT_global_H
+#define RT_global_H
+  #include <stdint.h>
+  #include <stdbool.h>
+
+  typedef unsigned int uint;
+
+  #define Local static
+  #define Free(pt) free(pt); (pt) = NULL;
+
+#endif
diff --git a/shared/tool/makefile/target_kernel-module.mk b/shared/tool/makefile/target_kernel-module.mk
new file mode 100644 (file)
index 0000000..b3684c4
--- /dev/null
@@ -0,0 +1,130 @@
+# make/target_kernel-module.mk — build *.kmod.c as kernel modules (single-pass, kmod-only)
+# invoked from $REPO_HOME/<role>
+# version 1.4
+
+.SUFFIXES:
+.DELETE_ON_ERROR:
+
+#--------------------------------------------------------------------------------
+# defaults for environment variables (override from outer make/env as needed)
+
+# Kernel build tree, which is part of the Linux system, use running kernel if unset
+KMOD_BUILD_DIR  ?= /lib/modules/$(shell uname -r)/build
+
+# Authored source directory (single dir)
+KMOD_SOURCE_DIR ?= cc
+
+# Extra compiler flags passed to Kbuild (e.g., -I $(KMOD_SOURCE_DIR))
+KMOD_CCFLAGS ?= 
+
+# Include *.lib.c into modules (1=yes, 0=no)
+KMOD_INCLUDE_LIB ?= 1
+
+# KMOD_OUTPUT_DIR is constrained, relative path, on the scratchpad, and ends in kmod
+# Require: non-empty, relative, no '..', ends with 'kmod' dir
+define assert_kmod_output_dir_ok
+  $(if $(strip $(1)),,$(error KMOD_OUTPUT_DIR is empty))
+  $(if $(filter /%,$(1)),$(error KMOD_OUTPUT_DIR must be relative: '$(1)'),)
+  $(if $(filter %/../% ../% %/.. ..,$(1)),$(error KMOD_OUTPUT_DIR must not contain '..': '$(1)'),)
+  $(if $(filter %/kmod %/kmod/ kmod,$(1)),,$(error KMOD_OUTPUT_DIR must end with 'kmod': '$(1)'))
+endef
+KMOD_OUTPUT_DIR ?= scratchpad/kmod
+$(eval $(call assert_kmod_output_dir_ok,$(KMOD_OUTPUT_DIR)))
+
+# The kernel make needs and absolute path to find the output directory
+ABS_KMOD_OUTPUT_DIR := $(CURDIR)/$(KMOD_OUTPUT_DIR)
+
+#--------------------------------------------------------------------------------
+# derived variables (computed from the above)
+
+# Authored basenames (without suffix)
+base_list := $(patsubst %.kmod.c,%,$(notdir $(wildcard $(KMOD_SOURCE_DIR)/*.kmod.c)))
+
+# Optional library sources (without suffix) to include inside modules
+ifeq ($(KMOD_INCLUDE_LIB),1)
+lib_base := $(patsubst %.lib.c,%,$(notdir $(wildcard $(KMOD_SOURCE_DIR)/*.lib.c)))
+else
+lib_base :=
+endif
+
+# Staged sources (kept namespaced to prevent .o collisions)
+all_kmod_c := $(addsuffix .kmod.c,$(addprefix $(KMOD_OUTPUT_DIR)/,$(base_list)))
+all_lib_c  := $(addsuffix .lib.c,$(addprefix $(KMOD_OUTPUT_DIR)/,$(lib_base)))
+
+
+
+#--------------------------------------------------------------------------------
+# targets
+
+.PHONY: usage
+usage:
+       @printf "Usage: make [kmod|clean|information|version]\n"
+
+.PHONY: version
+version:
+       @echo target_kmod version 1.4
+
+.PHONY: information
+information:
+       @echo "KMOD_SOURCE_DIR:   " $(KMOD_SOURCE_DIR)
+       @echo "KMOD_BUILD_DIR:    " $(KMOD_BUILD_DIR)
+       @echo "KMOD_OUTPUT_DIR:   " $(KMOD_OUTPUT_DIR)
+       @echo "base_list:    " $(base_list)
+       @echo "lib_base:     " $(lib_base)
+       @echo "all_kmod_c:   " $(all_kmod_c)
+       @echo "all_lib_c:    " $(all_lib_c)
+       @echo "KMOD_INCLUDE_LIB=" $(KMOD_INCLUDE_LIB)
+
+
+ifeq ($(strip $(base_list)),)
+  $(warning No *.kmod.c found under $(KMOD_SOURCE_DIR); nothing to build)
+endif
+
+# --- Parallel-safe preparation as real targets ---
+
+# ensure the staging dir exists (order-only prereq)
+$(KMOD_OUTPUT_DIR):
+       @mkdir -p "$(KMOD_OUTPUT_DIR)"
+
+# generate the Kbuild control Makefile
+$(KMOD_OUTPUT_DIR)/Makefile: | $(KMOD_OUTPUT_DIR)
+       @{ \
+         printf "ccflags-y += %s\n" "$(KMOD_CCFLAGS)"; \
+         printf "obj-m := %s\n" "$(foreach m,$(base_list),$(m).o)"; \
+         for m in $(base_list); do \
+           printf "%s-objs := %s.kmod.o" "$$m" "$$m"; \
+           for lb in $(lib_base); do printf " %s.lib.o" "$$lb"; done; \
+           printf "\n"; \
+         done; \
+       } > "$@"
+
+# stage kmod sources (one rule per file; parallelizable)
+$(KMOD_OUTPUT_DIR)/%.kmod.c: $(KMOD_SOURCE_DIR)/%.kmod.c | $(KMOD_OUTPUT_DIR)
+       @echo "--- Stage: $@ ---"
+       @cp -f "$(abspath $<)" "$@"
+
+# stage library sources (optional; also parallelizable)
+$(KMOD_OUTPUT_DIR)/%.lib.c: $(KMOD_SOURCE_DIR)/%.lib.c | $(KMOD_OUTPUT_DIR)
+       @echo "--- Stage: $@ ---"
+       @cp -f "$(abspath $<)" "$@"
+
+
+.PHONY: kmod
+kmod: $(KMOD_OUTPUT_DIR)/Makefile $(all_kmod_c) $(all_lib_c)
+ifeq ($(strip $(base_list)),)
+       @echo "--- No kmod sources; nothing to do ---"
+else
+       @echo "--- Invoking Kbuild for kmod: $(base_list) ---"
+       $(MAKE) -C "$(KMOD_BUILD_DIR)" M="$(ABS_KMOD_OUTPUT_DIR)" modules
+endif
+
+# quality-of-life: allow 'make scratchpad/kmod/foo.ko' after batch build
+$(KMOD_OUTPUT_DIR)/%.ko: kmod
+       @true
+
+.PHONY: clean
+clean:
+       @echo "Cleaning: $(KMOD_BUILD_DIR)"
+       @$(MAKE) -C "$(KMOD_BUILD_DIR)" M="$(ABS_KMOD_OUTPUT_DIR)" clean >/dev/null 2>&1 || true
+       @echo "Cleaning: $(KMOD_OUTPUT_DIR)"
+       @rm -rf -- "$(KMOD_OUTPUT_DIR)"
diff --git a/shared/tool/scratchpad b/shared/tool/scratchpad
new file mode 100755 (executable)
index 0000000..f14f140
--- /dev/null
@@ -0,0 +1,225 @@
+#!/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, subprocess
+
+HELP = """usage: scratchpad {ls|clear|help|make|write|size|find|lock|unlock} [ARGS]
+  ls               List scratchpad in an indented tree with perms and owner (quiet if missing).
+  clear            Remove all contents of scratchpad/ except top-level .gitignore.
+  clear NAME       Remove scratchpad/NAME only.
+  make [NAME]      Ensure scratchpad/ exists with .gitignore; with NAME, mkdir scratchpad/NAME.
+  write SRC [DST]  Copy file/dir SRC into scratchpad (to DST if given; parents created).
+  size             Print 'empty' if only .gitignore; else total bytes and item count.
+  find [OPTS...]   Run system 'find' rooted at scratchpad/ with OPTS (omit literal 'scratchpad').
+  lock PATH...     Attempt 'chattr +i' on given paths under scratchpad/ (no state kept).
+  unlock PATH...   Attempt 'chattr -i' on given paths under scratchpad/.
+
+Examples:
+  scratchpad make
+  scratchpad write ~/Downloads/test.tar.gz
+  scratchpad find -type f -mtime +30 -print   # files older than 30 days
+  scratchpad lock some/dir important.txt
+  scratchpad unlock some/dir important.txt
+"""
+
+CWD = os.getcwd()
+SP = os.path.join(CWD, "scratchpad")
+GITIGNORE = os.path.join(SP, ".gitignore")
+
+def have_sp() -> bool:
+  return os.path.isdir(SP)
+
+def ensure_sp():
+  os.makedirs(SP, exist_ok=True)
+  ensure_gitignore()
+
+def ensure_gitignore():
+  os.makedirs(SP, exist_ok=True)
+  if not os.path.isfile(GITIGNORE):
+    with open(GITIGNORE, "w", encoding="utf-8") as f:
+      f.write("*\n!.gitignore\n")
+
+def filemode(mode: int) -> str:
+  try:
+    return stat.filemode(mode)
+  except Exception:
+    return oct(mode & 0o777)
+
+def owner_group(st) -> str:
+  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 rel_depth(base: str, root: str) -> int:
+  rel = os.path.relpath(base, root)
+  return 0 if rel == "." else rel.count(os.sep) + 1
+
+def ls_tree(root: str) -> None:
+  if not have_sp():
+    return
+  print("scratchpad/")
+
+  def walk(path: str, indent: str, is_root: bool) -> None:
+    try:
+      it = list(os.scandir(path))
+    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:
+      # 1) root-level hidden files first
+      for f in (e for e in files if e.name.startswith(".")):
+        st = os.lstat(f.path)
+        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{f.name}")
+      # 2) then directories (and recurse so children sit under the parent)
+      for d in dirs:
+        st = os.lstat(d.path)
+        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{d.name}/")
+        walk(d.path, indent + '  ', False)
+      # 3) then non-hidden files
+      for f in (e for e in files if not e.name.startswith(".")):
+        st = os.lstat(f.path)
+        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{f.name}")
+    else:
+      # subdirs: keep previous order (dirs first, then files; dotfiles naturally sort first)
+      for d in dirs:
+        st = os.lstat(d.path)
+        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{d.name}/")
+        walk(d.path, indent + '  ', False)
+      for f in files:
+        st = os.lstat(f.path)
+        print(f"{filemode(st.st_mode)}  {owner_group(st)}  {indent}{f.name}")
+
+  walk(root, "  ", True)
+
+
+def clear_all() -> None:
+  if not have_sp():
+    return
+  for name in os.listdir(SP):
+    p = os.path.join(SP, name)
+    if name == ".gitignore" and os.path.isfile(p):
+      continue  # preserve only top-level .gitignore
+    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 clear_subdir(sub: str) -> None:
+  if not have_sp():
+    return
+  target = os.path.normpath(os.path.join(SP, sub))
+  try:
+    if os.path.commonpath([SP]) != os.path.commonpath([SP, target]):
+      return
+  except Exception:
+    return
+  if os.path.isdir(target) and not os.path.islink(target):
+    shutil.rmtree(target, ignore_errors=True)
+
+def cmd_make(args):
+  ensure_sp()
+  if args:
+    os.makedirs(os.path.join(SP, args[0]), exist_ok=True)
+
+def cmd_write(args):
+  if len(args) < 1:
+    print(HELP); return
+  if not have_sp():
+    ensure_sp()
+  src = args[0]
+  dst = args[1] if len(args) >= 2 else (os.path.basename(src.rstrip(os.sep)) or "untitled")
+  dst_path = os.path.normpath(os.path.join(SP, dst))
+  try:
+    if os.path.commonpath([SP]) != os.path.commonpath([SP, dst_path]):
+      print("refusing to write outside scratchpad", file=sys.stderr); return
+  except Exception:
+    print("invalid destination", file=sys.stderr); return
+  os.makedirs(os.path.dirname(dst_path), exist_ok=True)
+  if os.path.isdir(src):
+    if os.path.exists(dst_path):
+      shutil.rmtree(dst_path, ignore_errors=True)
+    shutil.copytree(src, dst_path, dirs_exist_ok=False)
+  else:
+    shutil.copy2(src, dst_path)
+
+def cmd_size():
+  if not have_sp():
+    return
+  names = os.listdir(SP)
+  if [n for n in names if n != ".gitignore"] == []:
+    print("empty"); return
+  total = 0; count = 0
+  for base, dirs, files in os.walk(SP):
+    for fn in files:
+      if fn == ".gitignore": 
+        continue
+      p = os.path.join(base, fn)
+      try:
+        total += os.path.getsize(p); count += 1
+      except OSError:
+        pass
+  print(f"bytes={total} items={count}")
+
+def cmd_find(args):
+  if not have_sp():
+    return
+  try:
+    subprocess.run(["find", SP] + args, check=False)
+  except FileNotFoundError:
+    print("find not available", file=sys.stderr)
+
+def cmd_chattr(flag: str, paths):
+  if not have_sp() or not paths:
+    return
+  try:
+    subprocess.run(["chattr", "-V"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
+  except FileNotFoundError:
+    print("chattr not available; lock/unlock skipped", file=sys.stderr); return
+  for rel in paths:
+    target = os.path.normpath(os.path.join(SP, rel))
+    try:
+      if os.path.commonpath([SP]) != os.path.commonpath([SP, target]):
+        continue
+    except Exception:
+      continue
+    try:
+      subprocess.run(["chattr", flag, target], check=False)
+    except Exception:
+      pass
+
+def CLI():
+  if len(sys.argv) < 2:
+    print(HELP); return
+  cmd, *args = sys.argv[1:]
+  if cmd == "ls":
+    if have_sp(): ls_tree(SP)
+    else: return
+  elif cmd == "clear":
+    if len(args) >= 1: clear_subdir(args[0])
+    else: clear_all()
+  elif cmd == "help":
+    print(HELP)
+  elif cmd == "make":
+    cmd_make(args)
+  elif cmd == "write":
+    cmd_write(args)
+  elif cmd == "size":
+    cmd_size()
+  elif cmd == "find":
+    cmd_find(args)
+  elif cmd == "lock":
+    cmd_chattr("+i", args)
+  elif cmd == "unlock":
+    cmd_chattr("-i", args)
+  else:
+    print(HELP)
+
+if __name__ == "__main__":
+  CLI()
diff --git a/shared/tool/setup b/shared/tool/setup
new file mode 100644 (file)
index 0000000..6cfc738
--- /dev/null
@@ -0,0 +1,120 @@
+#!/usr/bin/env bash
+script_afp=$(realpath "${BASH_SOURCE[0]}")
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+  echo "$script_afp:: This script must be sourced, not executed."
+  exit 1
+fi
+
+# without this bash takes non-matching globs literally
+shopt -s nullglob
+
+# does not presume sharing or world permissions
+umask 0077
+
+# --------------------------------------------------------------------------------
+# project definition
+
+# actual absolute directory path for this script file
+  script_adp(){
+    dirname "$script_afp"
+  }
+
+# This script is located at $REPO_HOME/shared/tool/setup.
+# Ascend two directories to discover the absolute $REPO_HOME path.
+  REPO_HOME=$(dirname "$(dirname "$(script_adp)")")
+  echo REPO_HOME "$REPO_HOME"
+
+  PROJECT=$(basename "$REPO_HOME")
+  echo PROJECT "$PROJECT"
+
+  # set the prompt decoration to the name of the project
+  PROMPT_DECOR=$PROJECT
+
+  export REPO_HOME PROJECT PROMPT_DECOR
+
+# --------------------------------------------------------------------------------
+# the following functions are provided for other scripts to use.
+# at the top of files that make use of these functions put the following line:
+#  script_afp=$(realpath "${BASH_SOURCE[0]}")
+#
+
+  ## script's filename
+  script_fn(){
+    basename "$script_afp"
+  }
+
+  ## script's file path relative to $REPO_HOME
+  script_fp(){
+    realpath --relative-to="${REPO_HOME}" "$script_afp"
+  }
+
+  ## script's dirpath relative to $REPO_HOME
+  script_dp(){
+    dirname "$(script_fp)"
+  }
+
+  export -f script_adp script_fn script_dp script_fp
+
+# --------------------------------------------------------------------------------
+# 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
+
+  # Sourcing is wrapped in a conditional guard to prevent failure on initial bootstrapping
+  installed_tool_sh="$REPO_HOME/$(script_dp)/PATH.sh"
+  if [[ -f "$installed_tool_sh" ]]; then
+    source "$installed_tool_sh"
+  fi
+
+  export PATH
+
+#--------------------------------------------------------------------------------
+# used by release scripts
+#
+  install_file() {
+    if [ "$#" -lt 3 ]; then
+      echo "env::install_file usage: install_file <source1> <source2> ... <target_dir> <permissions>"
+      return 1
+    fi
+
+    perms="${@: -1}"         # Last argument is permissions
+    target_dp="${@: -2:1}"   # Second-to-last argument is the target directory
+    sources=("${@:1:$#-2}")  # All other arguments are source files
+
+    if [ ! -d "$target_dp" ]; then
+      echo "env::install_file no install done: target directory '$target_dp' does not exist."
+      return 1
+    fi
+
+    for source_fp in "${sources[@]}"; do
+      if [ ! -f "$source_fp" ]; then
+        echo "env::install_file: source file '$source_fp' does not exist."
+        return 1
+      fi
+
+      target_file="$target_dp/$(basename "$source_fp")"
+
+      if ! install -m "$perms" "$source_fp" "$target_file"; then
+        echo "env::install_file: Failed to install $(basename "$source_fp") to $target_dp"
+        return 1
+      fi
+    done
+  }
+
+  export -f install_file
+
+# --------------------------------------------------------------------------------
+# closing
+#
+  if [[ -z "$ENV" ]]; then
+    export ENV=$(script_fp)
+  fi
diff --git a/shared/tool/version b/shared/tool/version
new file mode 100755 (executable)
index 0000000..91c6751
--- /dev/null
@@ -0,0 +1,4 @@
+echo "RT-style-JS_public v3.1 2026-06-20 10:39:27 Z"
+echo "Harmony v3.3 2026-06-21 13:20:18 Z"
+
+
diff --git a/shared/upstream/.gitignore b/shared/upstream/.gitignore
new file mode 100644 (file)
index 0000000..aa0e8eb
--- /dev/null
@@ -0,0 +1,2 @@
+*
+!/.gitignore
\ No newline at end of file
diff --git a/temp.sh b/temp.sh
deleted file mode 100755 (executable)
index fc2dc3d..0000000
--- a/temp.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-CLI(){
-  local doc_dirs=$(find . -type d -name "document" -not -path "*/\.git/*" -not -path "*/shared/third_party/*")
-
-  for doc_dir in $doc_dirs; do
-    local clean_dir="${doc_dir#./}"
-    
-    local rel_root=""
-    IFS='/' read -ra parts <<< "$clean_dir"
-    for part in "${parts[@]}"; do
-      rel_root="../${rel_root}"
-    done
-    
-    local setup_file="${clean_dir}/setup.js"
-    
-    cat <<EOF > "${setup_file}"
-window.RT_REPO_ROOT = "${rel_root}";
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/style_directory_dict.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/loader.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/body_visibility_hidden.js"></script>');
-EOF
-
-    echo "Wrote ${setup_file} using RT_REPO_ROOT = '${rel_root}'"
-  done
-}
-
-if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
-  CLI "$@"
-fi
diff --git a/tester/RT-formatter/RT-formatter b/tester/RT-formatter/RT-formatter
new file mode 100644 (file)
index 0000000..0451fcb
--- /dev/null
@@ -0,0 +1,307 @@
+#!/usr/bin/env -S python3 -B
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+RT_Format — Reasoning Technology code formatter (Shallow Tokenizer)
+
+Commands:
+  RT_Format write [--lisp] <file ...>      Format files in place (rewrite originals)
+  RT_Format copy  [--lisp] <file ...>      Save backups as <file>~ then format originals
+  RT_Format pipe  [--lisp]                 Read from stdin, write to stdout
+  RT_Format self_test                      Run built-in tests
+  RT_Format version                        Show tool version
+  RT_Format help | --help                  Show usage
+"""
+
+import sys ,re ,shutil ,os
+from typing import List ,Tuple ,Optional ,TextIO
+
+RTF_VERSION = "0.4.0-tokenized"
+
+USAGE = """\
+Usage:
+  RT_Format write [--lisp] <file ...>
+  RT_Format copy  [--lisp] <file ...>
+  RT_Format pipe  [--lisp]
+  RT_Format self_test
+  RT_Format version
+  RT_Format help | --help
+"""
+
+BR_OPEN  = "([{<"
+BR_CLOSE = ")]}>"
+PAIR = dict( zip(BR_OPEN ,BR_CLOSE) )
+REV  = dict( zip(BR_CLOSE ,BR_OPEN) )
+
+# --------------- Lexer ----------------
+
+class RT_Token:
+  def __init__(self ,kind: str ,text: str):
+    self.kind = kind
+    self.text = text
+
+  def __repr__(self):
+    return f"<{self.kind}:{repr(self.text)}>"
+
+# The regex prioritizes exact matches.
+# Comments include //, #, and /* ... */ blocks.
+# Strings include Python '''/""" blocks, plus standard single/double quotes.
+TOKEN_REGEX = re.compile(
+  r'(?P<COMMENT>//[^\n]*|#[^\n]*|(?s:/\*.*?\*/))'
+  r'|(?P<STRING>"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')'
+  r'|(?P<SPACE>[ \t]+)'
+  r'|(?P<NEWLINE>\n)'
+  r'|(?P<COMMA>,)'
+  r'|(?P<BR_OPEN>[\[\(\{<])'
+  r'|(?P<BR_CLOSE>[\]\)\}>])'
+  r'|(?P<CODE>[^ \t\n,\[\(\{<\]\)\}>"\'#/]+|/)'
+)
+
+def tokenize(text: str) -> List[RT_Token]:
+  tokens = []
+  for TM_match in TOKEN_REGEX.finditer(text):
+    kind = TM_match.lastgroup
+    text_val = TM_match.group(kind)
+    tokens.append( RT_Token(kind ,text_val) )
+  return tokens
+
+def group_lines( tokens: List[RT_Token] ) -> List[ List[RT_Token] ]:
+  lines = []
+  current = []
+  for TM_tok in tokens:
+    current.append(TM_tok)
+    if TM_tok.kind == "NEWLINE":
+      lines.append(current)
+      current = []
+  if current:
+    lines.append(current)
+  return lines
+
+# --------------- Formatting Passes ----------------
+
+def pass_vertical_commas( lines: List[List[RT_Token]] ) -> None:
+  for TM_idx in range( len(lines) - 1 ):
+    current_line = lines[TM_idx]
+    
+    # Find the last significant token
+    last_sig_idx = -1
+    for TM_i in range( len(current_line) - 1 ,-1 ,-1 ):
+      if current_line[TM_i].kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
+        last_sig_idx = TM_i
+        break
+    
+    if last_sig_idx>= 0 and current_line[last_sig_idx].kind == "COMMA":
+      # Remove the trailing comma
+      comma_tok = current_line.pop(last_sig_idx)
+      
+      # Migrate to the next line with code
+      for TM_j in range( TM_idx + 1 ,len(lines) ):
+        next_line = lines[TM_j]
+        first_sig_idx = -1
+        for TM_k ,TM_tok in enumerate(next_line):
+          if TM_tok.kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
+            first_sig_idx = TM_k
+            break
+        
+        if first_sig_idx>= 0:
+          next_line.insert(first_sig_idx ,comma_tok)
+          break
+
+def pass_horizontal_commas( line: List[RT_Token] ) -> None:
+  new_line = []
+  for TM_tok in line:
+    if TM_tok.kind == "COMMA":
+      is_vertical = all(t.kind == "SPACE" for t in new_line)
+      if not is_vertical:
+        while new_line and new_line[-1].kind == "SPACE":
+          new_line.pop()
+        if new_line:
+          new_line.append( RT_Token("SPACE" ," ") )
+      new_line.append(TM_tok)
+    elif TM_tok.kind == "SPACE":
+      if new_line and new_line[-1].kind == "COMMA":
+        continue # Drop space after comma
+      new_line.append(TM_tok)
+    else:
+      new_line.append(TM_tok)
+  line[:] = new_line
+
+def pass_tighten_brackets( line: List[RT_Token] ) -> None:
+  new_line = []
+  for TM_tok in line:
+    if TM_tok.kind == "SPACE":
+      if new_line and new_line[-1].kind == "BR_OPEN":
+        continue
+      new_line.append(TM_tok)
+    elif TM_tok.kind == "BR_CLOSE":
+      while new_line and new_line[-1].kind == "SPACE":
+        new_line.pop()
+      new_line.append(TM_tok)
+    else:
+      new_line.append(TM_tok)
+  line[:] = new_line
+
+def get_bracket_spans( line: List[RT_Token] ) -> List[ Tuple[int ,int] ]:
+  stack = []
+  spans = []
+  for TM_i ,TM_tok in enumerate(line):
+    if TM_tok.kind == "BR_OPEN":
+      stack.append( (TM_tok.text ,TM_i) )
+    elif TM_tok.kind == "BR_CLOSE":
+      if stack and REV[TM_tok.text] == stack[-1][0]:
+        _ ,pos = stack.pop()
+        if not stack:
+          spans.append( (pos ,TM_i) )
+  return spans
+
+def contains_inner_brackets( line: List[RT_Token] ,start: int ,end: int ) -> bool:
+  for TM_i in range(start + 1 ,end):
+    if line[TM_i].kind in ("BR_OPEN" ,"BR_CLOSE"):
+      return True
+  return False
+
+def pass_pad_outermost( line: List[RT_Token] ,is_lisp: bool ) -> None:
+  if is_lisp:
+    return
+  
+  while True:
+    spans = get_bracket_spans(line)
+    changed = False
+    
+    # Process from right to left to avoid shifting indices
+    for TM_start ,TM_end in reversed(spans):
+      if contains_inner_brackets(line ,TM_start ,TM_end):
+        left_has = (TM_start + 1 <len(line)) and (line[TM_start + 1].kind == "SPACE")
+        right_has = ( TM_end - 1>= 0 ) and ( line[TM_end - 1].kind == "SPACE" )
+        
+        if not left_has or not right_has:
+          if not right_has:
+            line.insert( TM_end ,RT_Token("SPACE" ," ") )
+          if not left_has:
+            line.insert( TM_start + 1 ,RT_Token("SPACE" ," ") )
+          changed = True
+          break # Re-evaluate spans after mutation
+    if not changed:
+      break
+
+# --------------- Public API ----------------
+
+def format_tokens( tokens: List[RT_Token] ,is_lisp: bool ) -> str:
+  lines = group_lines(tokens)
+  pass_vertical_commas(lines)
+  
+  for TM_line in lines:
+    pass_horizontal_commas(TM_line)
+    pass_tighten_brackets(TM_line)
+    pass_pad_outermost(TM_line ,is_lisp)
+    
+  return "".join(t.text for TM_line in lines for t in TM_line)
+
+def rt_format_text(text: str ,is_lisp: bool) -> str:
+  tokens = tokenize(text)
+  return format_tokens(tokens ,is_lisp)
+
+def rt_format_stream(inp: TextIO ,out: TextIO ,is_lisp: bool) -> None:
+  text = inp.read()
+  out.write( rt_format_text(text ,is_lisp) )
+
+# --------------- Self-test ----------------
+
+def run_self_test() -> bool:
+  ok = True
+  def chk(src ,exp):
+    nonlocal ok
+    got = rt_format_text(src ,False)
+    if got != exp:
+      print("FAIL:\n" + src + "\n=>\n" + got + "\nexpected:\n" + exp)
+      ok = False
+
+  chk("a,b,c" ,"a ,b ,c")
+  chk("a , b ,  c" ,"a ,b ,c")
+  chk("  ,vertical_arg" ,"  ,vertical_arg") 
+
+  chk("int a=0,\n  b=1,\n  c=2;" ,"int a=0\n  ,b=1\n  ,c=2;") 
+
+  chk("f ( x )" ,"f(x)")
+  chk("f(x) + g(y)" ,"f(x) + g(y)")
+  chk("  {" ,"  {") 
+
+  src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
+  exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
+  chk(src ,exp)
+
+  chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
+  chk("compute(x, f(y" ,"compute( x ,f(y") # Tolerant fragment fallback omitted for brevity, but structurally sound.
+
+  print("SELFTEST OK" if ok else "SELFTEST FAILED")
+  return ok
+
+# --------------- CLI ----------------
+
+def write_files( paths: List[str] ,is_lisp: bool ) -> int:
+  for TM_path in paths:
+    with open(TM_path ,"r" ,encoding="utf-8") as f:
+      data = f.read()
+    formatted = rt_format_text(data ,is_lisp)
+    with open(TM_path ,"w" ,encoding="utf-8") as f:
+      f.write(formatted)
+  return 0
+
+def copy_files( paths: List[str] ,is_lisp: bool ) -> int:
+  for TM_path in paths:
+    shutil.copy2(TM_path ,TM_path + "~")
+  return write_files(paths ,is_lisp)
+
+def get_usage() -> str:
+  prog_name = os.path.basename( sys.argv[0] )
+  return f"""\
+Usage:
+  {prog_name} write [--lisp] <file ...>
+  {prog_name} copy  [--lisp] <file ...>
+  {prog_name} pipe  [--lisp]
+  {prog_name} self_test
+  {prog_name} version
+  {prog_name} help | --help
+"""
+
+def CLI(argv=None) -> int:
+  args = list( sys.argv[1:] if argv is None else argv )
+  usage_text = get_usage()
+  
+  if not args or args[0] in {"help" ,"--help" ,"-h"}:
+    print(usage_text)
+    return 0
+
+  is_lisp = "--lisp" in args
+  args = [TM_a for TM_a in args if TM_a != "--lisp"]
+  
+  if not args:
+    return 0
+
+  cmd = args[0]
+  rest = args[1:]
+
+  if cmd == "version":
+    print(RT_FORMAT_VERSION)
+    return 0
+  if cmd == "self_test":
+    ok = run_self_test()
+    return 0 if ok else 1
+  if cmd == "pipe":
+    rt_format_stream(sys.stdin ,sys.stdout ,is_lisp)
+    return 0
+  if cmd == "write":
+    if not rest:
+      print("write: missing <file ...>\n" + usage_text)
+      return 2
+    return write_files(rest ,is_lisp)
+  if cmd == "copy":
+    if not rest:
+      print("copy: missing <file ...>\n" + usage_text)
+      return 2
+    return copy_files(rest ,is_lisp)
+
+  print(f"Unknown command: {cmd}\n" + usage_text)
+  return 2
+
+if __name__ == "__main__":
+  sys.exit( CLI() )
\ No newline at end of file
diff --git a/tester/RT-formatter/RT-formatter.el b/tester/RT-formatter/RT-formatter.el
new file mode 100644 (file)
index 0000000..94dfaaa
--- /dev/null
@@ -0,0 +1,5 @@
+( defun RT-format-buffer()
+  (interactive)
+  (save-excursion
+    ( shell-command-on-region(point-min)(point-max)
+                             "RT-formatter pipe" t t)) )
diff --git a/tester/RT-formatter/RT-formatter_alt.el b/tester/RT-formatter/RT-formatter_alt.el
new file mode 100644 (file)
index 0000000..dd0669e
--- /dev/null
@@ -0,0 +1,30 @@
+
+(defun RT-formatter-buffer ()
+  "Format the current buffer using RTfmt."
+  (interactive)
+  (if (not (executable-find "RT-formatter"))
+      (message "Error: RTfmt executable not found in PATH.")
+    (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
+          (args (list "pipe")))
+      (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
+        (setq args (append args (list "--lisp"))))
+      (unwind-protect
+          (let ((exit-code (apply #'call-process-region
+                                  (point-min) (point-max)
+                                  "RT-formatter"
+                                  nil temp-buffer nil
+                                  args)))
+            (if (zerop exit-code)
+                (let ((formatted-text (with-current-buffer temp-buffer (buffer-string))))
+                  (save-excursion
+                    (delete-region (point-min) (point-max))
+                    (insert formatted-text))
+                  (message "RT-formatter formatting successful."))
+              (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
+        (kill-buffer temp-buffer)))))
+
+;; ( defun RT-format-buffer()
+;;   (interactive)
+;;   (save-excursion
+;;     ( shell-command-on-region(point-min)(point-max)
+;;                              "RT-formatter pipe" t t)) )
diff --git a/tester/RT-formatter/RT-formatter_script.el b/tester/RT-formatter/RT-formatter_script.el
new file mode 100644 (file)
index 0000000..45ff29b
--- /dev/null
@@ -0,0 +1,22 @@
+(defun RT-formatter-buffer ()
+  "Format the current buffer using RTfmt."
+  (interactive)
+  (if (not (executable-find "RT-formatter"))
+      (message "Error: RTfmt executable not found in PATH.")
+    (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
+          (args (list "pipe")))
+      (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
+        (setq args (append args (list "--lisp"))))
+      (unwind-protect
+          (let ((exit-code (apply #'call-process-region
+                                  (point-min) (point-max)
+                                  "RT-formatter"
+                                  nil temp-buffer nil
+                                  args)))
+            (if (zerop exit-code)
+                (progn
+                  ;; Applies a non-destructive diff, preserving point and markers natively
+                  (replace-buffer-contents temp-buffer)
+                  (message "RT-formatter formatting successful."))
+              (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
+        (kill-buffer temp-buffer)))))
diff --git a/tester/RT-formatter/RT-formatter_with-compare b/tester/RT-formatter/RT-formatter_with-compare
new file mode 100644 (file)
index 0000000..09ee7a5
--- /dev/null
@@ -0,0 +1,331 @@
+#!/usr/bin/env -S python3 -B
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+RTfmt — Reasoning Technology code formatter (Predicate Tokenizer)
+
+Commands:
+  RTfmt write [--lisp] <file ...>      Format files in place (rewrite originals)
+  RTfmt copy  [--lisp] <file ...>      Save backups as <file>~ then format originals
+  RT-formatter pipe  [--lisp]                 Read from stdin, write to stdout
+  RTfmt self_test                      Run built-in tests
+  RTfmt version                        Show tool version
+  RTfmt help | --help                  Show usage
+"""
+
+import sys ,re ,shutil ,os
+from typing import List ,Tuple ,Optional ,TextIO
+
+RTF_VERSION = "0.5.0-predicate"
+
+def get_usage() -> str:
+  prog_name = os.path.basename(sys.argv[0])
+  return f"""\
+Usage:
+  {prog_name} write [--lisp] <file ...>
+  {prog_name} copy  [--lisp] <file ...>
+  {prog_name} pipe  [--lisp]
+  {prog_name} self_test
+  {prog_name} version
+  {prog_name} help | --help
+"""
+
+# Removed < and > so they are treated as standard CODE operators
+BR_OPEN  = "([{"
+BR_CLOSE = ")]}"
+PAIR = dict( zip(BR_OPEN ,BR_CLOSE) )
+REV  = dict( zip(BR_CLOSE ,BR_OPEN) )
+
+# --------------- Lexer ----------------
+
+class RT_Token:
+  def __init__(self ,kind: str ,text: str):
+    self.kind = kind
+    self.text = text
+
+  def __repr__(self):
+    return f"<{self.kind}:{repr(self.text)}>"
+
+TOKEN_REGEX = re.compile(
+  r'(?P<COMMENT>//[^\n]*|#[^\n]*|(?s:/\*.*?\*/))'
+  r'|(?P<STRING>"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')'
+  r'|(?P<SPACE>[ \t]+)'
+  r'|(?P<NEWLINE>\n)'
+  r'|(?P<COMMA>,)'
+  r'|(?P<BR_OPEN>[\[\(\{])'
+  r'|(?P<BR_CLOSE>[\]\)\}])'
+  r'|(?P<CODE>[^ \t\n,\[\(\{\]\)\}"\'#/]+|/)'
+)
+
+def tokenize(text: str) -> List[RT_Token]:
+  tokens = []
+  for TM_match in TOKEN_REGEX.finditer(text):
+    kind = TM_match.lastgroup
+    text_val = TM_match.group(kind)
+    tokens.append( RT_Token(kind ,text_val) )
+  return tokens
+
+# --------------- Intelligence API ----------------
+
+class TokenStream:
+  def __init__(self ,tokens: List[RT_Token]):
+    self.tokens = tokens
+
+  def get_token(self ,index: int) -> Optional[RT_Token]:
+    if 0 <= index < len(self.tokens):
+      return self.tokens[index]
+    return None
+
+  def next_sig_index(self ,index: int) -> Optional[int]:
+    for TM_i in range(index + 1 ,len(self.tokens)):
+      if self.tokens[TM_i].kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
+        return TM_i
+    return None
+
+  def is_first_on_line(self ,index: int) -> bool:
+    for TM_i in range(index - 1 ,-1 ,-1):
+      k = self.tokens[TM_i].kind
+      if k == "NEWLINE":
+        return True
+      if k != "SPACE":
+        return False
+    return True # Start of file
+
+  def indent_of_line(self ,index: int) -> str:
+    for TM_i in range(index ,-1 ,-1):
+      if self.tokens[TM_i].kind == "NEWLINE":
+        if TM_i + 1 < len(self.tokens) and self.tokens[TM_i + 1].kind == "SPACE":
+          return self.tokens[TM_i + 1].text
+        return ""
+    if self.tokens and self.tokens[0].kind == "SPACE":
+      return self.tokens[0].text
+    return ""
+
+  def indent_of_left_match(self ,index: int) -> Optional[str]:
+    tok = self.get_token(index)
+    if not tok or tok.kind != "BR_CLOSE":
+      return None
+    target_opener = REV[tok.text]
+    depth = 0
+    for TM_i in range(index - 1 ,-1 ,-1):
+      t = self.tokens[TM_i]
+      if t.kind == "BR_CLOSE":
+        depth += 1
+      elif t.kind == "BR_OPEN":
+        if depth > 0:
+          depth -= 1
+        elif t.text == target_opener:
+          return self.indent_of_line(TM_i)
+    return None
+
+# --------------- Rule Engine ----------------
+
+def rule_migrate_vertical_commas(stream: TokenStream):
+  TM_i = 0
+  while TM_i < len(stream.tokens):
+    if stream.tokens[TM_i].kind == "COMMA":
+      is_trailing = False
+      next_sig = stream.next_sig_index(TM_i)
+      if next_sig is not None:
+        for TM_j in range(TM_i + 1 ,next_sig):
+          if stream.tokens[TM_j].kind == "NEWLINE":
+            is_trailing = True
+            break
+      
+      if is_trailing:
+        comma_tok = stream.tokens.pop(TM_i)
+        next_sig -= 1 # Shifted because of pop
+        stream.tokens.insert(next_sig ,comma_tok)
+        continue
+    TM_i += 1
+
+def rule_format_horizontal_commas(stream: TokenStream):
+  for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+    if stream.tokens[TM_i].kind == "COMMA":
+      if stream.is_first_on_line(TM_i):
+        continue
+      
+      next_tok = stream.get_token(TM_i + 1)
+      if next_tok and next_tok.kind == "SPACE":
+        stream.tokens.pop(TM_i + 1)
+      
+      prev_tok = stream.get_token(TM_i - 1)
+      if prev_tok and prev_tok.kind == "SPACE":
+        if prev_tok.text != " ":
+          prev_tok.text = " "
+      else:
+        stream.tokens.insert(TM_i ,RT_Token("SPACE" ," "))
+
+def rule_fix_closing_indent(stream: TokenStream):
+  for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+    if stream.tokens[TM_i].kind == "BR_CLOSE" and stream.is_first_on_line(TM_i):
+      target_indent = stream.indent_of_left_match(TM_i)
+      if target_indent is not None:
+        prev = stream.get_token(TM_i - 1)
+        if prev and prev.kind == "SPACE":
+          prev.text = target_indent
+        else:
+          stream.tokens.insert(TM_i ,RT_Token("SPACE" ,target_indent))
+
+def rule_tighten_brackets(stream: TokenStream):
+  for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+    if stream.tokens[TM_i].kind == "SPACE" and not stream.is_first_on_line(TM_i):
+      prev_t = stream.get_token(TM_i - 1)
+      next_t = stream.get_token(TM_i + 1)
+      if (prev_t and prev_t.kind == "BR_OPEN") or (next_t and next_t.kind == "BR_CLOSE"):
+        stream.tokens.pop(TM_i)
+
+def get_bracket_spans(stream: TokenStream) -> List[Tuple[int ,int]]:
+  stack = []
+  spans = []
+  for TM_i ,tok in enumerate(stream.tokens):
+    if tok.kind == "BR_OPEN":
+      stack.append( (tok.text ,TM_i) )
+    elif tok.kind == "BR_CLOSE":
+      if stack and REV[tok.text] == stack[-1][0]:
+        _ ,pos = stack.pop()
+        if not stack:
+          spans.append( (pos ,TM_i) )
+  return spans
+
+def rule_pad_outermost(stream: TokenStream ,is_lisp: bool):
+  if is_lisp:
+    return
+  while True:
+    spans = get_bracket_spans(stream)
+    changed = False
+    for TM_start ,TM_end in reversed(spans):
+      has_inner = False
+      for TM_k in range(TM_start + 1 ,TM_end):
+        if stream.tokens[TM_k].kind in ("BR_OPEN" ,"BR_CLOSE"):
+          has_inner = True
+          break
+      
+      if has_inner:
+        left_has = (TM_start + 1 < len(stream.tokens)) and stream.tokens[TM_start + 1].kind == "SPACE"
+        right_has = (TM_end - 1 >= 0) and stream.tokens[TM_end - 1].kind == "SPACE"
+        if not left_has or not right_has:
+          if not right_has:
+            stream.tokens.insert(TM_end ,RT_Token("SPACE" ," "))
+          if not left_has:
+            stream.tokens.insert(TM_start + 1 ,RT_Token("SPACE" ," "))
+          changed = True
+          break
+    if not changed:
+      break
+
+# --------------- Public API ----------------
+
+def format_tokens(tokens: List[RT_Token] ,is_lisp: bool) -> str:
+  stream = TokenStream(tokens)
+  
+  rule_migrate_vertical_commas(stream)
+  rule_format_horizontal_commas(stream)
+  rule_tighten_brackets(stream)
+  rule_fix_closing_indent(stream)
+  rule_pad_outermost(stream ,is_lisp)
+  
+  return "".join(t.text for t in stream.tokens)
+
+def rt_format_text(text: str ,is_lisp: bool) -> str:
+  tokens = tokenize(text)
+  return format_tokens(tokens ,is_lisp)
+
+def rt_format_stream(inp: TextIO ,out: TextIO ,is_lisp: bool) -> None:
+  text = inp.read()
+  out.write( rt_format_text(text ,is_lisp) )
+
+# --------------- Self-test ----------------
+
+def run_self_test() -> bool:
+  ok = True
+  def chk(src ,exp):
+    nonlocal ok
+    got = rt_format_text(src ,False)
+    if got != exp:
+      print("FAIL:\n" + src + "\n=>\n" + got + "\nexpected:\n" + exp)
+      ok = False
+
+  chk("a,b,c" ,"a ,b ,c")
+  chk("a , b ,  c" ,"a ,b ,c")
+  chk("  ,vertical_arg" ,"  ,vertical_arg") 
+
+  chk("int a=0,\n  b=1,\n  c=2;" ,"int a=0\n  ,b=1\n  ,c=2;") 
+
+  chk("f ( x )" ,"f(x)")
+  chk("f(x) + g(y)" ,"f(x) + g(y)")
+  chk("  {" ,"  {") 
+
+  src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
+  exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
+  chk(src ,exp)
+
+  chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
+  
+  # Operator protection check
+  chk("for(int TM = 0; TM < count; ++TM)" ,"for(int TM = 0; TM < count; ++TM)")
+
+  print("SELFTEST OK" if ok else "SELFTEST FAILED")
+  return ok
+
+# --------------- CLI ----------------
+def write_files(paths: List[str] ,is_lisp: bool) -> int:
+  for TM_path in paths:
+    with open(TM_path ,"r" ,encoding="utf-8") as f:
+      data = f.read()
+    
+    formatted = rt_format_text(data ,is_lisp)
+    
+    # Only touch the file if the content actually changed
+    if data != formatted:
+      with open(TM_path ,"w" ,encoding="utf-8") as f:
+        f.write(formatted)
+      print(f"Formatted: {TM_path}")
+  return 0
+
+def copy_files(paths: List[str] ,is_lisp: bool) -> int:
+  for TM_path in paths:
+    shutil.copy2(TM_path ,TM_path + "~")
+  return write_files(paths ,is_lisp)
+
+def CLI(argv=None) -> int:
+  args = list(sys.argv[1:] if argv is None else argv)
+  usage_text = get_usage()
+  
+  if not args or args[0] in {"help" ,"--help" ,"-h"}:
+    print(usage_text)
+    return 0
+
+  is_lisp = "--lisp" in args
+  args = [TM_a for TM_a in args if TM_a != "--lisp"]
+  
+  if not args:
+    return 0
+
+  cmd = args[0]
+  rest = args[1:]
+
+  if cmd == "version":
+    print(RTF_VERSION)
+    return 0
+  if cmd == "self_test":
+    ok = run_self_test()
+    return 0 if ok else 1
+  if cmd == "pipe":
+    rt_format_stream(sys.stdin ,sys.stdout ,is_lisp)
+    return 0
+  if cmd == "write":
+    if not rest:
+      print("write: missing <file ...>\n" + usage_text)
+      return 2
+    return write_files(rest ,is_lisp)
+  if cmd == "copy":
+    if not rest:
+      print("copy: missing <file ...>\n" + usage_text)
+      return 2
+    return copy_files(rest ,is_lisp)
+
+  print(f"Unknown command: {cmd}\n" + usage_text)
+  return 2
+
+if __name__ == "__main__":
+  sys.exit( CLI() )
diff --git a/tester/RT-formatter/RT-formatter_with-compare.el b/tester/RT-formatter/RT-formatter_with-compare.el
new file mode 100644 (file)
index 0000000..36213ae
--- /dev/null
@@ -0,0 +1,23 @@
+(defun RT-formatter-buffer ()
+  "Format the current buffer using RTfmt."
+  (interactive)
+  (if (not (executable-find "RT-formatter"))
+      (message "Error: RTfmt executable not found in PATH.")
+    (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
+          (args (list "pipe")))
+      (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
+        (setq args (append args (list "--lisp"))))
+      (unwind-protect
+          (let ((exit-code (apply #'call-process-region
+                                  (point-min) (point-max)
+                                  "RT-formatter"
+                                  nil temp-buffer nil
+                                  args)))
+            (if (zerop exit-code)
+                ;; Check if the formatted text is actually different
+                (if (= (compare-buffer-substrings nil nil nil temp-buffer nil nil) 0)
+                    (message "RTfmt: Already perfectly formatted.")
+                  (replace-buffer-contents temp-buffer)
+                  (message "RT-formatter formatting successful."))
+              (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
+        (kill-buffer temp-buffer)))))
diff --git a/tester/RT-formatter/data_test-0.c b/tester/RT-formatter/data_test-0.c
new file mode 100644 (file)
index 0000000..c877406
--- /dev/null
@@ -0,0 +1,20 @@
+// commas and simple tight brackets
+int g(){
+  int a=0 ,
+    b=1 ,
+    c=2; 
+  return h(a ,b ,c);
+}
+
+// balanced outermost-with-nesting -> pad inside outer ()
+int f(){ return outer(inner(a ,b)); }
+
+// strings and comments must be unchanged
+int s(){ printf("x ,y ,z (still a string)"); /* a ,b ,c */ return 1; }
+
+// unbalanced open-right with nesting -> pad after first unmatched '('
+int u(){if(doit(foo(1 ,2)  // missing )) 
+  return 0;}
+
+// arrays / subscripts stay tight; commas still RT-style
+int a(int i ,int j){ return M[i ,j] + V[i] + W[j]; }
diff --git a/tester/RT-formatter/data_test-1.py b/tester/RT-formatter/data_test-1.py
new file mode 100644 (file)
index 0000000..9b2fa87
--- /dev/null
@@ -0,0 +1,16 @@
+# commas and spacing in defs / calls
+def f ( x , y , z ):
+    return dict( a =1 , b= 2 ), [ 1, 2 ,3 ], ( (1,2) )
+
+# outermost-with-nesting -> pad inside outer ()
+val = outer( inner( a,b ) )
+
+# strings/comments untouched
+s = "text, with , commas ( not to touch )"  # a ,b ,c
+
+# unbalanced: open-left (closing without opener) -> no padding unless inner bracket before it
+def g():
+    return result)  # likely unchanged
+
+# unbalanced: open-right (first unmatched opener) with inner bracket following
+k = compute(x, f(y