From: Thomas Walker Lynch Date: Fri, 21 Nov 2025 10:03:38 +0000 (+0000) Subject: moving to authored/loadable ontology X-Git-Url: https://git.reasoningtechnology.com/style/rt_dark_doc.css?a=commitdiff_plain;h=312217c7d6c87167851a15df44ca427f32d2e2fd;p=Harmony.git moving to authored/loadable ontology --- diff --git a/0pus_Harmony_skeleton b/0pus_Harmony_skeleton deleted file mode 100644 index e69de29..0000000 diff --git a/developer/authored/.gitkeep b/developer/authored/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/developer/authored/hello.cli.c b/developer/authored/hello.cli.c new file mode 100644 index 0000000..a626cac --- /dev/null +++ b/developer/authored/hello.cli.c @@ -0,0 +1,2 @@ +#include +int main(void){ puts("hello from Rabbit CLI"); return 0; } diff --git a/developer/cc/.githolder b/developer/cc/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/developer/cc/hello.cli.c b/developer/cc/hello.cli.c deleted file mode 100644 index a626cac..0000000 --- a/developer/cc/hello.cli.c +++ /dev/null @@ -1,2 +0,0 @@ -#include -int main(void){ puts("hello from Rabbit CLI"); return 0; } diff --git a/developer/document/.githolder b/developer/document/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/developer/document/.gitkeep b/developer/document/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/developer/document/ontology.org b/developer/document/ontology.org new file mode 100644 index 0000000..f1aae70 --- /dev/null +++ b/developer/document/ontology.org @@ -0,0 +1,69 @@ +#+TITLE: Project Ontology: Authored vs. Loadable +#+AUTHOR: Harmony Developer +#+DATE: 2025-11-21 +#+OPTIONS: toc:t num:nil + +* The Core Philosophy +This project moves away from "techno-idiosyncratic" directory names (like ~source~, ~bin~, or ~src~) in favor of names that describe the *fundamental invariants* of the files they contain. + +We distinguish files based on two properties: +1. **Provenance**: Who created this file? +2. **Capability**: What can the system do with this file? + +**The Golden Rule:** +- **God, Artists, and SQL** (and Developers) *create* things. +- **Factories** (Build Systems) *make* things. + +* Directory Structure Overview + +#+BEGIN_SRC text +developer/ +├── authored/ # (The Truth) Human-written code. +├── loadable/ # (The Entry Points) Script links & architecture-independent runners. +├── scratchpad/ +│ └── loadable/ # (The Heavy Machinery) Compiled binaries. Ignored by Git. +└── tool/ # (The Factory) Build scripts and orchestrators. +#+END_SRC + +* Detailed Definitions + +** ~developer/authored/~ +This directory contains the *primary truth* of the project. +- **Invariant:** Every file here is written by a human author (the Artist). +- **Rule:** Build scripts generally have *read-only* access to this directory. They should never modify files here. +- **Contents:** C source files (~*.c~), Python modules (~*.py~), Header files (~*.h~). + +** ~developer/loadable/~ +This directory contains the *entry points* for the system. +- **Invariant:** Files here possess the property of being "loadable" (runnable) by the user or the OS. +- **Contents:** + - Symlinks to interpreted code (e.g., links to ~../authored/CLI.py~). + - Shell wrappers. + - Architecture-independent runners. + +** ~developer/scratchpad/loadable/~ +This directory contains the *machine-generated* executables. +- **Invariant:** These files are derived/synthesized by the factory (Make). +- **Rule:** This directory is **ignored by Git**. It prevents binary blobs and architecture-specific files from polluting the repository. +- **Contents:** Compiled C binaries, object files, and temporary build artifacts. + +* Workflow Examples + +** Scenario A: Working with C (Compiled) +1. **Author:** You write ~my_program.c~ in ~developer/authored/~. +2. **Build:** You run ~tool/make~. +3. **Result:** The compiler reads ~authored/my_program.c~ and synthesizes a binary at ~developer/scratchpad/loadable/my_program~. + - *Note:* This binary is transient. If you delete ~scratchpad/~, it can be rebuilt. + +** Scenario B: Working with Python (Interpreted) +1. **Author:** You write ~my_script.CLI.py~ in ~developer/authored/~. +2. **Setup:** You (or a setup script) create a semantic link. +3. **Result:** A link exists at ~developer/loadable/my_script~ pointing to the authored file. + - *Note:* This makes the script "loadable" from the system path without moving the source of truth. + +* The Release Process +When the project is ready for distribution, the release script collects items from both sources: +- It takes the *logic* from ~authored~. +- It takes the *binaries* from ~scratchpad/loadable~. +- It places them into the final ~$REPO_HOME/release/~ sub directories for the end-user. +It is common that the developer customizes the release script. diff --git a/developer/document/python packages b/developer/document/python packages deleted file mode 100644 index 83a0dd0..0000000 --- a/developer/document/python packages +++ /dev/null @@ -1,2 +0,0 @@ - -pip install isqrt diff --git a/developer/experiment/.githolder b/developer/experiment/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/developer/experiment/.gitkeep b/developer/experiment/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/developer/python3/decompose.py b/developer/python3/decompose.py deleted file mode 100644 index f979189..0000000 --- a/developer/python3/decompose.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import random -from isqrt import isqrt - -def decompose(n0_h ,r1_p ,acc_car): - - if r0_h < 0 : print "r0_h is negative" - if r1_p < 0 : print "r1_p is negative" - if acc_car != 0 and acc_car != 1 : print "acc_car is not 0 or 1" - - k_n0_h = n0_h.bit_length() - k_r1_p = r1_p.bit_length() - - if acc_car : - k_n1_p = k_ - - return z1 ,n1_p - - -def main(): - return 1 - -if __name__ == "__main__": - main() diff --git a/developer/python3/iroot.py b/developer/python3/iroot.py deleted file mode 100644 index a6ae3e8..0000000 --- a/developer/python3/iroot.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import random -from isqrt import isqrt - -def iroot(z): - c = isqrt(z) - r = z - n0_h ** 2 - return c ,r - -def main(): - return 1 - -if __name__ == "__main__": - main() diff --git a/developer/python3/test_env.py b/developer/python3/test_env.py deleted file mode 100644 index d47e8a1..0000000 --- a/developer/python3/test_env.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys - -def print_env_var(name): - value = os.getenv(name) - print(f"{name:<16}: {value if value else ''}") - -def main(): - print("=== Python Environment Test ===") - print(f"Python executable : {sys.executable}") - print(f"Python version : {sys.version}") - print() - - print("=== Harmony Environment Variables ===") - for var in ["ROLE", "REPO_HOME", "PYTHON_HOME", "VIRTUAL_ENV", "ENV"]: - print_env_var(var) - - print() - print("=== Current Working Directory ===") - print(os.getcwd()) - -if __name__ == "__main__": - main() diff --git a/developer/python3/test_isqrt.py b/developer/python3/test_isqrt.py deleted file mode 100644 index adac4ad..0000000 --- a/developer/python3/test_isqrt.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import random -from isqrt import isqrt - -def test_isqrt(num_tests, max_bits): - failed = 0 - passed = 0 - - for i in range(1, num_tests + 1): - bits = random.randint(1, max_bits) - n = random.getrandbits(bits) - - root = isqrt(n) - lower = root * root - upper = (root + 1) * (root + 1) - - if lower <= n < upper: - passed += 1 - else: - print(f"\n❌ Test failed:") - print(f"bits = {bits}") - print(f"n = {hex(n)}") - print(f"root = {hex(root)}") - if lower > n: - print("Reason = root is too large") - elif upper <= n: - print("Reason = root + 1 is too small") - failed += 1 - - if i % 1000 == 0: - print(".", end="", flush=True) - - if num_tests >= 10: - print() - - print(f"✅ Passed: {passed}") - print(f"❌ Failed: {failed}") - -def print_root(n): - print_bin(isqrt(n)) - -def reduce(n): - n - -def main(): - if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} ") - sys.exit(1) - - try: - n = int(sys.argv[1], 0) - max = int(sys.argv[2], 0) - except ValueError: - print("Error: argument must be a valid integer.") - sys.exit(1) - - if n < 0: - print("Error: number of tests must be non-negative.") - sys.exit(1) - - if max < 4: - print("Error: max_bits must be at least 4.") - sys.exit(1) - - test_isqrt(n, max) - -if __name__ == "__main__": - main() diff --git a/developer/python3/test_reduce.py b/developer/python3/test_reduce.py deleted file mode 100644 index 02cb53e..0000000 --- a/developer/python3/test_reduce.py +++ /dev/null @@ -1,70 +0,0 @@ - -import sys -import random -from iroot import iroot - -def test_reduce(num_tests, max_bits): - failed = 0 - passed = 0 - - hex_width = (max_bits + 3) // 4 # ceil(max_bits / 4) for hex digit count - hex_widtho2 = (max_bits + 7) // 8 # ceil(max_bits / 8) for reduced-width fields - - field = f"#0{hex_width + 2}x" - fieldo2 = f"#0{hex_widtho2 + 2}x" - - header = ( - f"{'z':>{hex_width + 2}} " - f"{'n0h':>{hex_widtho2 + 2}} " - f"{'r_h':>{hex_widtho2 + 2}} " - f"{'r_hp':>{hex_widtho2 + 2}}" - ) - print(header) - print("-" * len(header)) - - for i in range(1, num_tests + 1): - bits = random.randint(1, max_bits) - z = random.getrandbits(bits) - - n0h, r_h, r_hp = iroot(z) - - # Format fields, fallback to full-width hex if overflow detected - z_str = format(z, field) if z.bit_length() <= max_bits else f"{z:#x}" - n0h_str = format(n0h, fieldo2) if n0h.bit_length() <= max_bits else f"{n0h:#x}" - r_h_str = format(r_h, fieldo2) if r_h.bit_length() <= max_bits else f"{r_h:#x}" - r_hp_str = format(r_hp, fieldo2) if r_hp.bit_length() <= max_bits else f"{r_hp:#x}" - - print(f"{z_str} {n0h_str} {r_h_str} {r_hp_str}") - - if i % 1000 == 0: - print(".", end="", flush=True) - - if num_tests >= 1000: - print() - - - -def main(): - if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} ") - sys.exit(1) - - try: - n = int(sys.argv[1], 0) - max = int(sys.argv[2], 0) - except ValueError: - print("Error: argument must be a valid integer.") - sys.exit(1) - - if n < 0: - print("Error: number of tests must be non-negative.") - sys.exit(1) - - if max < 4: - print("Error: max_bits must be at least 4.") - sys.exit(1) - - test_reduce(n, max) - -if __name__ == "__main__": - main() diff --git a/developer/tool/.githolder b/developer/tool/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/developer/tool/.gitkeep b/developer/tool/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/document/.githolder b/document/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/document/.gitkeep b/document/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/release/authored/.gitkeep b/release/authored/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/release/documnt/.gitkeep b/release/documnt/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/release/loadable/.gitkeep b/release/loadable/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/release/local_build/.gitignore b/release/local_build/.gitignore new file mode 100644 index 0000000..bea49c8 --- /dev/null +++ b/release/local_build/.gitignore @@ -0,0 +1,3 @@ + +* +!/.gitignore \ No newline at end of file diff --git a/release/python3/.githolder b/release/python3/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/release/shell/.githolder b/release/shell/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/tester/.githolder b/tester/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/tester/.gitkeep b/tester/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tool/source_skeleton_compare/command.py b/tool/source_skeleton_compare/command.py index 969c70a..0ebb856 100644 --- a/tool/source_skeleton_compare/command.py +++ b/tool/source_skeleton_compare/command.py @@ -302,7 +302,12 @@ def build_export_commands( cp_cmds: List[str] = [] other_list: List[str] = [] - for rel_path, a_info in A_tree.items(): + # Sort keys to ensure parent directories are processed before their children. + sorted_paths = sorted(A_tree.keys(), key=len) + included_dirs: Set[str] = set() + + for rel_path in sorted_paths: + a_info = A_tree[rel_path] a_type = a_info.get("node_type") rel_display = rel_path if rel_path else "." @@ -316,12 +321,27 @@ def build_export_commands( ) continue + # Check if this path is a child of an already included directory + is_child_of_included_dir = False + for d in included_dirs: + if rel_path.startswith(d + os.sep): + is_child_of_included_dir = True + break + + if is_child_of_included_dir: + continue + # Case 2: A directory if a_type == "directory": if b_info is None: - # Missing in B: safe to mkdir -p - target_dir = os.path.join(B_root, rel_path) if rel_path else B_root - mkdir_cmds.append(f"mkdir -p {shell_quote(target_dir)}") + # Missing in B: copy the directory recursively. + src = os.path.join(A_root, rel_path) if rel_path else A_root + # The destination should be the parent directory in B. + dst = os.path.dirname(os.path.join(B_root, rel_path)) + mkdir_cmds.append( + f"cp -a {shell_quote(src)} {shell_quote(dst + os.sep)}" + ) + included_dirs.add(rel_path) else: # Exists in B: must also be directory if b_type != "directory": @@ -334,9 +354,10 @@ def build_export_commands( if b_info is None: # A-only file src = os.path.join(A_root, rel_path) if rel_path else A_root - dst = B_root + # Destination is the parent directory in B, with a trailing slash + dst = os.path.dirname(os.path.join(B_root, rel_path)) cp_cmds.append( - f"cp --parents -a {shell_quote(src)} {shell_quote(dst)}/" + f"cp -a {shell_quote(src)} {shell_quote(dst + os.sep)}" ) continue @@ -353,9 +374,10 @@ def build_export_commands( if isinstance(a_mtime, (int, float)) and isinstance(b_mtime, (int, float)): if a_mtime > b_mtime: src = os.path.join(A_root, rel_path) if rel_path else A_root - dst = B_root + # Destination is the parent directory in B, with a trailing slash + dst = os.path.dirname(os.path.join(B_root, rel_path)) cp_cmds.append( - f"cp --parents -a {shell_quote(src)} {shell_quote(dst)}/" + f"cp -a {shell_quote(src)} {shell_quote(dst + os.sep)}" ) return mkdir_cmds, cp_cmds, other_list @@ -382,7 +404,7 @@ def cmd_export( print(f"# B root: {B_root}") print("#") - print("# Directories to create in B (mkdir -p):") + print("# Directories to copy from A -> B (cp -a):") if mkdir_cmds: for line in mkdir_cmds: print(line) @@ -390,7 +412,7 @@ def cmd_export( print("# (none)") print("#") - print("# Files to copy from A -> B (cp --parents -a):") + print("# Files to copy from A -> B (cp -a):") if cp_cmds: for line in cp_cmds: print(line) diff --git a/tool/source_skeleton_compare/manus_2025_11_20_215471873762383.txt b/tool/source_skeleton_compare/manus_2025_11_20_215471873762383.txt new file mode 100644 index 0000000..70e1f99 --- /dev/null +++ b/tool/source_skeleton_compare/manus_2025_11_20_215471873762383.txt @@ -0,0 +1,33 @@ +Conversation with Manus +Started on November 20, 2025 at 08:56 PM Singapore time +08 (GMT+0800) + +--- + +08:56 PM | Thomas Lynch: Hello, I signed up today with 7 days free. However, in the middle of a chat, I was prompted to sign up, which I already did. It then charged me for the month, but we have not been 7 days yet, and it is full charge. Why did it do that? + As a second question is the 'credits'. Did it prompt again because the credits were used? Am I now on the hook for two months of charges in less than a day of use? + If just a few hours use uses up a 'month' of credits, then I really can't afford Manus, and need to back out of this. + +08:56 PM | Manus AI: I understand your concern about the unexpected charge! Let me clarify what likely happened. + About the charge: Our subscriptions operate on a credit-based system (http://help.manus.im/en/articles/11711097-what-are-the-rules-for-credits-consumption-and-how-can-i-obtain-them) where credits are consumed during active task processing. When you run out of credits (even during a trial), you may be prompted to upgrade to continue using the service, which would trigger billing. + You're not charged twice: The billing is monthly from your subscription date (http://help.manus.im/en/articles/11711089-where-can-i-find-the-invoice-after-purchase-can-the-invoice-be-modified), so you'll only have one active subscription. + To check your usage: Visit Settings > Usage to see exactly how your credits were consumed and review transaction details. + Your options: + - Cancel subscription: (http://help.manus.im/en/articles/11711087-how-can-i-cancel-my-subscription) Go to your profile > Account > Manage subscription > Cancel subscription to stop future charges. +- Request refund: You can submit a refund request with your invoice information and reason. We'll review it according to our refund policy. + The good news is you can cancel anytime to prevent future charges, and we do consider refund requests on a case-by-case basis! + +Sources: +What are the rules for credits consumption? And how can I obtain them? (http://help.manus.im/en/articles/11711097-what-are-the-rules-for-credits-consumption-and-how-can-i-obtain-them) +Where can I check my credit consumption/usage? (http://help.manus.im/en/articles/12520235-where-can-i-check-my-credit-consumption-usage) +How can I request a refund for a purchased membership? (http://help.manus.im/en/articles/11711102-how-can-i-request-a-refund-for-a-purchased-membership) +How can I cancel my subscription? (http://help.manus.im/en/articles/11711087-how-can-i-cancel-my-subscription) +Where can I find the invoice after purchase, can the invoice be modified? (http://help.manus.im/en/articles/11711089-where-can-i-find-the-invoice-after-purchase-can-the-invoice-be-modified) + +08:57 PM | Liora from Manus: We will respond to you with the resolution within 48 hours. Please wait for our update! + +09:01 PM | Thomas Lynch: You charged my $46.80, the plan says it costs $33.00 explain. + +09:02 PM | Thomas Lynch: hello? + +--- +Exported from Manus on November 20, 2025 at 09:03 PM Singapore time +08 (GMT+0800) \ No newline at end of file diff --git a/tool_shared/bespoke/.githolder b/tool_shared/bespoke/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/tool_shared/bespoke/.gitkeep b/tool_shared/bespoke/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tool_shared/document/.githolder b/tool_shared/document/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/tool_shared/document/.gitkeep b/tool_shared/document/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tool_shared/patch/.githolder b/tool_shared/patch/.githolder deleted file mode 100644 index e69de29..0000000 diff --git a/tool_shared/patch/.gitkeep b/tool_shared/patch/.gitkeep new file mode 100644 index 0000000..e69de29