diff --git a/.claude/skills/bear-observe/harness/tree.php b/.claude/skills/bear-observe/harness/tree.php index d38057e5..f59c898c 100644 --- a/.claude/skills/bear-observe/harness/tree.php +++ b/.claude/skills/bear-observe/harness/tree.php @@ -6,22 +6,37 @@ /** * Renders an observation log as a tree, naming the type that closes each scope. * - * Usage: php tree.php [log.json] (default: the newest latest.json under var/log) + * Usage: php tree.php [log.json] [--full] (default: the newest latest.json under var/log) * * `stree` prints the closing context but not its type, and here the type carries the answer: * `get` closed by `cache_hit` and `get` closed by `cache_miss` have the same context keys. + * + * `--full` disables the 60-char value truncation; without it, a cut value is marked + * `...(+N)` with the number of characters dropped, so it reads as cut rather than as a + * value that happened to end there. */ $argvList = $argv; array_shift($argvList); +$full = false; +$positional = []; +foreach ((is_array($argvList) ? $argvList : []) as $arg) { + if ($arg === '--full') { + $full = true; + continue; + } + + $positional[] = $arg; +} + function fail(string $message): never { fwrite(STDERR, "FAIL {$message}\n"); exit(1); } -$file = $argvList[0] ?? null; +$file = $positional[0] ?? null; if ($file === null) { $found = glob(getcwd() . '/var/log/*/observe/latest.json') ?: []; $found !== [] || fail('no var/log/*/observe/latest.json — run a request first, or pass a path'); @@ -34,17 +49,47 @@ function fail(string $message): never is_array($log) || fail("not JSON: {$file}"); // Runs in the application's PHP, which may be a no-dev install without ext-mbstring. -function truncate(string $text): string +function truncate(string $text, bool $full): string { + if ($full) { + return $text; + } + if (function_exists('mb_substr')) { - return mb_strlen($text) > 60 ? mb_substr($text, 0, 57) . '...' : $text; + $length = mb_strlen($text); + + return $length > 60 ? mb_substr($text, 0, 57) . '...(+' . ($length - 57) . ')' : $text; + } + + $length = strlen($text); + + return $length > 60 ? substr($text, 0, 57) . '...(+' . ($length - 57) . ')' : $text; +} + +// A value whose whole string is backslash-joined identifiers is a FQCN, not a URI, a JSON +// blob, or free text — none of those parse as this pattern. +function shortenFqcn(string $text): string +{ + if (! str_contains($text, '\\') || preg_match('/^(\\\\?[A-Za-z_][A-Za-z0-9_]*)+$/', $text) !== 1) { + return $text; + } + + $pos = strrpos($text, '\\'); + + return $pos === false ? $text : substr($text, $pos + 1); +} + +function formatScalar(bool|int|float|string $value): string +{ + if (is_bool($value)) { + return $value ? 'true' : 'false'; } - return strlen($text) > 60 ? substr($text, 0, 57) . '...' : $text; + return (string) $value; } /** @param array $context */ -function summarize(array $context): string +function summarize(array $context, bool $full): string { $parts = []; foreach ($context as $key => $value) { @@ -52,19 +97,21 @@ function summarize(array $context): string continue; } - $text = is_scalar($value) ? (string) $value : (string) json_encode($value, JSON_UNESCAPED_SLASHES); + $text = is_scalar($value) ? formatScalar($value) : (string) json_encode($value, JSON_UNESCAPED_SLASHES); if ($key === 'body_ref') { $text = basename($text); + } elseif (is_string($value)) { + $text = shortenFqcn($text); } - $parts[] = $key . '=' . truncate($text); + $parts[] = $key . '=' . truncate($text, $full); } return implode(' ', $parts); } /** @param array $node */ -function render(array $node, string $indent = ''): void +function render(array $node, bool $full, string $indent = ''): void { // Events are recorded against the enclosing scope, so they belong under it, ahead of the // scopes it went on to open. @@ -80,12 +127,12 @@ function render(array $node, string $indent = ''): void $next = $indent . ($i === $last ? ' ' : '│ '); $close = $child['close'] ?? null; $arrow = is_array($close) && is_string($close['type'] ?? null) - ? ' → ' . $close['type'] . ' ' . summarize((array) ($close['context'] ?? [])) + ? ' → ' . $close['type'] . ' ' . summarize((array) ($close['context'] ?? []), $full) : ''; - echo $indent, $branch, $child['type'], ' ', summarize((array) ($child['context'] ?? [])), $arrow, "\n"; - render($child, $next); + echo $indent, $branch, $child['type'], ' ', summarize((array) ($child['context'] ?? []), $full), $arrow, "\n"; + render($child, $full, $next); } } echo $file, "\n"; -render($log); +render($log, $full); diff --git a/be/composer.json b/be/composer.json index 3c4a7769..d18f566a 100644 --- a/be/composer.json +++ b/be/composer.json @@ -1,28 +1,28 @@ { - "name": "my-vendor/be-mart-be", - "type": "library", - "license": "MIT", - "description": "BeMart Be — pure Be Framework domain (Input / Final / Semantic / Exception / Becoming / Reason) for the BeMart project.", - "require": { - "php": "^8.3", - "be-framework/be": "dev-semantic-logger-0.9 as 0.1.0", - "ray/input-query": "^1.0", - "ray/media-query": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "autoload": { - "psr-4": { - "MyVendor\\BeMart\\Be\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "MyVendor\\BeMart\\Be\\Tests\\": "tests/" - } - }, - "config": { - "sort-packages": true + "name": "my-vendor/be-mart-be", + "type": "library", + "license": "MIT", + "description": "BeMart Be — pure Be Framework domain (Input / Final / Semantic / Exception / Becoming / Reason) for the BeMart project.", + "require": { + "php": "^8.3", + "be-framework/be": "0.x-dev as 0.1.0", + "ray/input-query": "^1.0", + "ray/media-query": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "autoload": { + "psr-4": { + "MyVendor\\BeMart\\Be\\": "src/" } + }, + "autoload-dev": { + "psr-4": { + "MyVendor\\BeMart\\Be\\Tests\\": "tests/" + } + }, + "config": { + "sort-packages": true + } } diff --git a/composer.json b/composer.json index c9078d51..659ff8de 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ ], "require": { "php": "^8.3", - "be-framework/be": "dev-semantic-logger-0.9 as 0.1.0", + "be-framework/be": "0.x-dev as 0.1.0", "bear/devtools": "dev-master", "bear/package": "^1.14", "bear/query-repository": "1.x-dev", diff --git a/composer.lock b/composer.lock index cba493e2..8e417172 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f2d015bd9a2cfd9d5a3a1af24c60a709", + "content-hash": "6bcbec6c34b040019ca68b7803e304ad", "packages": [ { "name": "aura/cli", @@ -502,16 +502,16 @@ }, { "name": "be-framework/be", - "version": "dev-semantic-logger-0.9", + "version": "0.x-dev", "source": { "type": "git", "url": "https://github.com/be-framework/Be.Framework.git", - "reference": "d6ca6d263119b8a687d9daa7284b424c6f9f9395" + "reference": "60fd5690d2a83442d645c8f678894a3222d7eefd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/be-framework/Be.Framework/zipball/d6ca6d263119b8a687d9daa7284b424c6f9f9395", - "reference": "d6ca6d263119b8a687d9daa7284b424c6f9f9395", + "url": "https://api.github.com/repos/be-framework/Be.Framework/zipball/60fd5690d2a83442d645c8f678894a3222d7eefd", + "reference": "60fd5690d2a83442d645c8f678894a3222d7eefd", "shasum": "" }, "require": { @@ -530,6 +530,7 @@ "phpunit/phpunit": "^12.2", "vimeo/psalm": "^6.0@dev" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -553,9 +554,9 @@ "description": "Be Framework - The Ontological Programming Framework for PHP", "support": { "issues": "https://github.com/be-framework/Be.Framework/issues", - "source": "https://github.com/be-framework/Be.Framework/tree/semantic-logger-0.9" + "source": "https://github.com/be-framework/Be.Framework/tree/0.x" }, - "time": "2026-08-17T03:12:22+00:00" + "time": "2026-09-19T03:18:30+00:00" }, { "name": "bear/app-meta", @@ -2061,10 +2062,10 @@ "dist": { "type": "path", "url": "be", - "reference": "582b54fe20c555993e476620e427f520c37864e1" + "reference": "2af920313bc6a519e1c9e11346cbbfe54095528a" }, "require": { - "be-framework/be": "dev-semantic-logger-0.9 as 0.1.0", + "be-framework/be": "0.x-dev as 0.1.0", "php": "^8.3", "ray/input-query": "^1.0", "ray/media-query": "^1.1" @@ -7292,12 +7293,12 @@ "source": { "type": "git", "url": "https://github.com/bearsunday/BEAR.EventSourcing.git", - "reference": "8616c825999227de2f7b065445082b177c479f01" + "reference": "e811a13a47c08f4795bcd223d31b7778a68ba30c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bearsunday/BEAR.EventSourcing/zipball/8616c825999227de2f7b065445082b177c479f01", - "reference": "8616c825999227de2f7b065445082b177c479f01", + "url": "https://api.github.com/repos/bearsunday/BEAR.EventSourcing/zipball/e811a13a47c08f4795bcd223d31b7778a68ba30c", + "reference": "e811a13a47c08f4795bcd223d31b7778a68ba30c", "shasum": "" }, "require": { @@ -7378,7 +7379,7 @@ "issues": "https://github.com/bearsunday/BEAR.EventSourcing/issues", "source": "https://github.com/bearsunday/BEAR.EventSourcing" }, - "time": "2026-09-17T09:48:05+00:00" + "time": "2026-09-19T03:17:37+00:00" }, { "name": "composer/pcre", @@ -9518,7 +9519,7 @@ "aliases": [ { "package": "be-framework/be", - "version": "dev-semantic-logger-0.9", + "version": "0.9999999.9999999.9999999-dev", "alias": "0.1.0", "alias_normalized": "0.1.0.0" },