owfadev 0.0.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/asset-read/SKILL.md +39 -0
- package/.agents/skills/check/SKILL.md +130 -0
- package/.agents/skills/docs/SKILL.md +24 -0
- package/.agents/skills/map/SKILL.md +72 -0
- package/.agents/skills/plugin/SKILL.md +74 -0
- package/.agents/skills/profile/SKILL.md +75 -0
- package/.agents/skills/query/SKILL.md +48 -0
- package/.agents/skills/save/SKILL.md +94 -0
- package/.agents/skills/wiki_plugin_kernel/SKILL.md +104 -0
- package/.meta/VERSION +1 -0
- package/.meta/command/asset-read/SKILL.md +39 -0
- package/.meta/command/check/SKILL.md +130 -0
- package/.meta/command/docs/SKILL.md +24 -0
- package/.meta/command/map/SKILL.md +72 -0
- package/.meta/command/plugin/SKILL.md +74 -0
- package/.meta/command/profile/SKILL.md +75 -0
- package/.meta/command/query/SKILL.md +48 -0
- package/.meta/command/save/SKILL.md +94 -0
- package/.meta/command/wiki_plugin_kernel/SKILL.md +104 -0
- package/.meta/docs/agent-behavior.md +68 -0
- package/.meta/docs/agents-example.md +34 -0
- package/.meta/docs/conversion.md +24 -0
- package/.meta/docs/deployment.md +311 -0
- package/.meta/docs/developer.md +33 -0
- package/.meta/docs/distribution.md +132 -0
- package/.meta/docs/intro.md +56 -0
- package/.meta/docs/log-example.md +19 -0
- package/.meta/docs/map.md +23 -0
- package/.meta/docs/mechanics.md +180 -0
- package/.meta/docs/naming.md +113 -0
- package/.meta/docs/packages.md +140 -0
- package/.meta/docs/quickstart.md +79 -0
- package/.meta/docs/research-landscape.md +132 -0
- package/.meta/docs/research-user-profile.md +27 -0
- package/.meta/docs/sandbox.md +40 -0
- package/.meta/docs/usage.md +45 -0
- package/.meta/plugins/core/calendar/PLUGIN.md +35 -0
- package/.meta/plugins/core/calendar/PLUGIN.yaml +21 -0
- package/.meta/plugins/core/cron/PLUGIN.md +38 -0
- package/.meta/plugins/core/cron/PLUGIN.yaml +24 -0
- package/.meta/plugins/core/device/PLUGIN.md +38 -0
- package/.meta/plugins/core/device/PLUGIN.yaml +21 -0
- package/.meta/plugins/core/device/scripts/check.py +29 -0
- package/.meta/plugins/core/domain/PLUGIN.md +43 -0
- package/.meta/plugins/core/domain/PLUGIN.yaml +8 -0
- package/.meta/plugins/core/hot/PLUGIN.md +40 -0
- package/.meta/plugins/core/hot/PLUGIN.yaml +15 -0
- package/.meta/plugins/core/index/PLUGIN.md +39 -0
- package/.meta/plugins/core/index/PLUGIN.yaml +18 -0
- package/.meta/plugins/core/instance-git/PLUGIN.md +39 -0
- package/.meta/plugins/core/instance-git/PLUGIN.yaml +15 -0
- package/.meta/plugins/core/instance-git/scripts/check.py +90 -0
- package/.meta/plugins/core/language/PLUGIN.md +38 -0
- package/.meta/plugins/core/language/PLUGIN.yaml +23 -0
- package/.meta/plugins/core/language/scripts/check.py +23 -0
- package/.meta/plugins/core/link/PLUGIN.md +39 -0
- package/.meta/plugins/core/link/PLUGIN.yaml +16 -0
- package/.meta/plugins/core/link/scripts/check.py +122 -0
- package/.meta/plugins/core/log/PLUGIN.md +58 -0
- package/.meta/plugins/core/log/PLUGIN.yaml +18 -0
- package/.meta/plugins/core/log/scripts/check.py +21 -0
- package/.meta/plugins/core/mapping/PLUGIN.md +32 -0
- package/.meta/plugins/core/mapping/PLUGIN.yaml +24 -0
- package/.meta/plugins/core/mapping/scripts/check.py +90 -0
- package/.meta/plugins/core/notes/PLUGIN.md +42 -0
- package/.meta/plugins/core/notes/PLUGIN.yaml +19 -0
- package/.meta/plugins/core/sessions/PLUGIN.md +63 -0
- package/.meta/plugins/core/sessions/PLUGIN.yaml +20 -0
- package/.meta/plugins/core/sessions/scripts/check.py +33 -0
- package/.meta/plugins/core/structure/PLUGIN.md +29 -0
- package/.meta/plugins/core/structure/PLUGIN.yaml +18 -0
- package/.meta/plugins/core/structure/scripts/check.py +32 -0
- package/.meta/plugins/core/tag/PLUGIN.md +37 -0
- package/.meta/plugins/core/tag/PLUGIN.yaml +18 -0
- package/.meta/plugins/core/tag/scripts/check.py +28 -0
- package/.meta/plugins/core/tmp/PLUGIN.md +26 -0
- package/.meta/plugins/core/tmp/PLUGIN.yaml +16 -0
- package/.meta/plugins/core/todo/PLUGIN.md +33 -0
- package/.meta/plugins/core/todo/PLUGIN.yaml +22 -0
- package/.meta/plugins/core/trust/PLUGIN.md +70 -0
- package/.meta/plugins/core/trust/PLUGIN.yaml +23 -0
- package/.meta/plugins/core/trust/scripts/check.py +77 -0
- package/.meta/plugins/core/user-profile/PLUGIN.md +39 -0
- package/.meta/plugins/core/user-profile/PLUGIN.yaml +30 -0
- package/.meta/plugins/core/vault/PLUGIN.md +28 -0
- package/.meta/plugins/core/vault/PLUGIN.yaml +13 -0
- package/.meta/plugins/core/wiki/PLUGIN.md +37 -0
- package/.meta/plugins/core/wiki/PLUGIN.yaml +8 -0
- package/.meta/protocol/actions.md +42 -0
- package/.meta/protocol/experiments.md +40 -0
- package/.meta/protocol/registry.yaml +75 -0
- package/.meta/scripts/pipeline.py +428 -0
- package/.meta/scripts/wiki_plugin_kernel.py +1891 -0
- package/.meta/scripts/wikilib.py +79 -0
- package/.meta/tests/test_install_preflight.py +177 -0
- package/LICENSE +21 -21
- package/README.md +96 -15
- package/README.zh-CN.md +103 -0
- package/bin/owfa.js +594 -0
- package/package.json +36 -7
- package/templates/AGENTS.template.md +19 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# hot: Hot Cache
|
|
2
|
+
|
|
3
|
+
## Design Summary
|
|
4
|
+
|
|
5
|
+
- **Why it exists**: the recent-change digest page — the lowest-cost entry for an agent entering the repository, read hot first then dig deeper as needed; opposite the index retrieval entry, hot is the entry to "now". Merely a cache: if lost, rebuildable from log and the repository
|
|
6
|
+
- **Key rulings**:
|
|
7
|
+
- Hard-cap trio — 25 entries, 5 days, 200 characters per entry: parameters calibrated against the original repository's measurements — per-entry median 389 characters, maximum 4959, runaway proven; the mechanical authoritative source is pipeline.py
|
|
8
|
+
- The evict-before-write duty: outside the window means delete; eviction and truncation are performed mechanically by the script, no asking — the cache does not bloat
|
|
9
|
+
- The only derived page with hand-written links: broken links are checked but it is not an inbound-link source, per the link contract
|
|
10
|
+
|
|
11
|
+
## Structure
|
|
12
|
+
|
|
13
|
+
- Single file `wiki/hot.md`, organized in sections — Recent map, Recent save, Recent query, Recent check, etc.
|
|
14
|
+
- Entry: date plus wikilink plus a one-sentence essence, at most 200 characters, a hard cap
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- Wholly regenerable: hot is merely a cache; if lost, rebuildable from log and the repository
|
|
19
|
+
- Rolling window: outside the window means delete; eviction and truncation are performed mechanically by the script, no asking
|
|
20
|
+
|
|
21
|
+
## Config
|
|
22
|
+
|
|
23
|
+
```yaml config
|
|
24
|
+
hot.max_entries: 25 # mechanical authoritative source in pipeline.py; the script source is the rule list
|
|
25
|
+
hot.max_days: 5
|
|
26
|
+
hot.max_entry_chars: 200 # parameters calibrated against the original repository's measurements: per-entry median 389 chars, max 4959, runaway proven
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Changelog
|
|
30
|
+
|
|
31
|
+
- 0.10 2026-09-23: added the wiki dependency edge — inner-side plugins attach to wiki, aligning with the domain 0.1 declaration; omitted from the 2026-09-22 domain-ization batch
|
|
32
|
+
- 0.9 2026-09-13: usage's type parameter gained its value-set source — same as log, pointing to the log block of the AGENTS injection region
|
|
33
|
+
- 0.8 2026-09-13: injection source moved to the manifest — removed Checks, Usage, Inject, Attachments sections, md returned to pure documentation
|
|
34
|
+
- 0.7 2026-09-13: established the "Usage" section — the write-side contract is projected by the command's injection region, a single text source
|
|
35
|
+
- 0.6 2026-09-12: manifest dropped layer — layering abolished: injection order changed to dependency topology plus alphabetical order, direction checks removed
|
|
36
|
+
- 0.5 2026-09-12: identifiers anglicized — section headers, attached-audit contract keys, type enums, pipeline call parameters
|
|
37
|
+
- 0.4 2026-09-10: writing mechanized — via pipeline.py hot, eviction and truncation performed by the script; the authoritative source of parameters moved to the script source
|
|
38
|
+
- 0.3 2026-09-10: manifest gained layer: derived — layering established: derived layer, zero dependencies
|
|
39
|
+
- 0.2 2026-09-09: per-entry length cap of 200 characters, plus the evict-before-write duty written down
|
|
40
|
+
- 0.1 2026-09-08: converted from the original wiki hot structure
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
id: hot
|
|
2
|
+
schema_version: 1
|
|
3
|
+
version: 0.10
|
|
4
|
+
depends:
|
|
5
|
+
- wiki
|
|
6
|
+
updated: 2026-10-06
|
|
7
|
+
attachment:
|
|
8
|
+
- "wiki/hot.md — single-file hot cache"
|
|
9
|
+
fields: {}
|
|
10
|
+
inject: "Hot cache `wiki/hot.md`: recent-change digest (≤25 entries, <5 days, ≤200 chars per entry); agents read this page first on entering the repository; evict out-of-window entries before writing"
|
|
11
|
+
usage:
|
|
12
|
+
- Writing entries (mechanical, automatic): `python .meta/scripts/pipeline.py hot <type> "<wikilink + one-sentence essence>"` (type value set same as log, see the log block of the AGENTS injection region); window eviction and truncation are performed by the script
|
|
13
|
+
checks:
|
|
14
|
+
- Window out of bounds (>25 entries / >5 days / >200 chars per entry) → run `pipeline.py hot <type> "<backfill>"` once, or wait for the next write to converge naturally then re-check
|
|
15
|
+
- Contradiction with log (log has records while hot shows no trace) → warning
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# index: Index
|
|
2
|
+
|
|
3
|
+
## Design Summary
|
|
4
|
+
|
|
5
|
+
- **Why it exists**: a cheap retrieval entry, the second layer of query, a pointer structure for drilling down level by level. A derived-layer aggregation page — born of the parent's offloading need, not a directory's "supposed duty"
|
|
6
|
+
- **Key rulings**:
|
|
7
|
+
- Overflow-offloading scheme, final shape in 0.10: a small repository lists all content directly in the root index, one page serves all — the engineering repository went 4-to-1, test-repo measured 8-to-1; wherever content grows, the disclosure boundary appears there. The cure is always subdirectories, never index changes
|
|
8
|
+
- Pure-function rebuild, never hand-edited: the offloading split is a function of the structural status quo; same structure, same result, no historical state
|
|
9
|
+
- Indexes do not invent structure: an index only reflects structure, never hints how directories should be organized — silent on content quality
|
|
10
|
+
- The root index is the version self-statement spot: format_version is the page-format contract version, bumped on incompatible changes
|
|
11
|
+
|
|
12
|
+
## Structure
|
|
13
|
+
|
|
14
|
+
- The root `wiki/index.md` is always present: a format_version frontmatter, i.e. the page-format contract version, bumped on incompatible changes — the root index is the version self-statement spot; it directly lists reachable pages, taking in this directory and un-split subtrees, full-path wikilinks
|
|
15
|
+
- Other directories' `index.md` are born of overflow offloading: when some index list — page entries plus directory entries — overflows the window, subtrees are split out in descending page-count order, ties by name order, into their own index, until it fits; applies recursively
|
|
16
|
+
- Entry = wikilink plus description — frontmatter `description` first, falling back to the body's first non-empty non-structural line truncated at 80 characters; concept pages grouped by type; a split-out subdirectory's entry line carries the subtree count; an empty subtree keeps a 0-page visibility line, with no link, outside the broken-link graph
|
|
17
|
+
- `wiki/tags.md`: the tag-to-page reverse index, aggregating the tag plugin's field
|
|
18
|
+
|
|
19
|
+
## Invariants
|
|
20
|
+
|
|
21
|
+
- Index pages are wholly regenerable, never hand-edited: aggregate only, originate nothing; pure-function rebuild — the offloading split is a function of the structural status quo, same structure same result, no historical state
|
|
22
|
+
- Window parameters defer to the `pipeline.py` source code; the script source is the rule list; obsolete old indexes after merge-back into the parent are mechanically deleted on rebuild
|
|
23
|
+
- Indexes do not invent structure: the existence and content of an index only reflect structure, never hint how directories should be organized; when a level's flat files overflow the window, they are listed over the window as-is; splitting directories belongs to humans and territory plugins
|
|
24
|
+
- Consistent with the actual page set; reserved-name files, i.e. index and log, the wiki root's derived pages, i.e. hot and tags, the archive/ subtree, and the tmp/ temporary zone are invisible to the derived layer: not concept pages, not indexed
|
|
25
|
+
|
|
26
|
+
## Changelog
|
|
27
|
+
|
|
28
|
+
- 0.13 (2026-10-06) inject line compressed to pointer density (issue #12 layer discipline) — procedural detail lives in usage / PLUGIN.md / skills- 0.12 2026-09-23: added the wiki dependency edge — inner-side plugins attach to wiki, aligning with the domain 0.1 declaration; omitted from the 2026-09-22 domain-ization batch
|
|
29
|
+
- 0.11 2026-09-19: concept-page detection excluded wiki/tmp/ — the temporary zone stays out of indexes and tags, in step with the tmp plugin's establishment
|
|
30
|
+
- 0.10 2026-09-19: the per-directory scheme changed to the overflow-offloading scheme — list window of at most M, subtree-descending splits, the root always present, empty-subtree visibility lines; small repositories collapse to a single index, the engineering repository 4-to-1, test-repo measured 8-to-1; invariants gained "pure-function rebuild" and "indexes do not invent structure"
|
|
31
|
+
- 0.9 2026-09-13: the format_version semantics entered the in-repository disclosure — expert review: no provenance inside the instance; index-description truncation gained an ellipsis, pipeline `_cut`
|
|
32
|
+
- 0.8 2026-09-13: injection source moved to the manifest — removed Checks, Usage, Inject, Attachments sections, md returned to pure documentation
|
|
33
|
+
- 0.7 2026-09-13: established the "Usage" section — the write-side contract is projected by the command's injection region, a single text source
|
|
34
|
+
- 0.6 2026-09-13: the root index's version self-statement field renamed format_version — the format contract internalized, the plugin's external contract references zeroed
|
|
35
|
+
- 0.5 2026-09-12: manifest dropped layer — layering abolished: injection order changed to dependency topology plus alphabetical order, direction checks removed
|
|
36
|
+
- 0.4 2026-09-12: identifiers anglicized — section headers, attached-audit contract keys, type enums, pipeline call parameters
|
|
37
|
+
- 0.3 2026-09-10: per-directory indexing, progressive disclosure — root page carrying the version self-statement field, subdirectories carrying counts; rebuilds scripted as pipeline.py index and tags, LLMs never hand-write
|
|
38
|
+
- 0.2 2026-09-10: manifest gained layer: derived — layering established: derived layer, depending only on tag
|
|
39
|
+
- 0.1 2026-09-08: converted from the original wiki index, i.e. the master-catalog rules; changed to whole rebuilds only, no incremental writes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
id: index
|
|
2
|
+
schema_version: 1
|
|
3
|
+
version: 0.13
|
|
4
|
+
depends:
|
|
5
|
+
- tag
|
|
6
|
+
- wiki
|
|
7
|
+
updated: 2026-10-06
|
|
8
|
+
attachment:
|
|
9
|
+
- "wiki/index.md — root index (always present; carries format_version; directly lists reachable pages)"
|
|
10
|
+
- "Per-directory index.md — born of the overflow-offloading scheme (split out when a list overflows the window, disappearing on rebuild once merged back into the parent)"
|
|
11
|
+
- "wiki/tags.md — tag reverse index"
|
|
12
|
+
fields: {}
|
|
13
|
+
inject: "Index overflow-offloading: root wiki/index.md always present (carries format_version), listing all reachable pages full-path; a list over the window (params in pipeline source) splits subtrees into their own index.md by descending page count; wiki/tags.md unchanged; aggregate only, never hand-edited, pure-function rebuild (pipeline.py index); indexes invent no structure — overflow lists in full as-is, the cure is subdirectories"
|
|
14
|
+
usage:
|
|
15
|
+
- Post-write rebuild (mechanical, automatic): `python .meta/scripts/pipeline.py index` (index — overflow-offloading scheme, including deletion of obsolete old indexes after merge-back) and the same script's `tags` (tag reverse index); LLMs never hand-write indexes
|
|
16
|
+
checks:
|
|
17
|
+
- Deviation between indexes and the actual page set (including merged-back indexes pending deletion) → fixed by running `pipeline.py index` to rebuild (idempotent; no diff means consistent); same for tags (`pipeline.py tags`)
|
|
18
|
+
- Traces of hand editing → warning
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# instance-git: the snapshot ledger of real use
|
|
2
|
+
|
|
3
|
+
The instance library's git is a **snapshot surface** — a time machine and an undo button, not a collaboration tool. This plugin declares the contract and mechanically audits the red lines; it never executes git writes itself, and neither does the kernel (the journal is the kernel's own ledger).
|
|
4
|
+
|
|
5
|
+
## Three authors, one ledger
|
|
6
|
+
|
|
7
|
+
| Snapshot | Author | When | Scope |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| kernel snapshot | — none. The kernel never touches git; its mechanical facts live in the kernel journal | — | — |
|
|
10
|
+
| session snapshot | the agent, on the user's behalf | every transaction close (package transactions after validate goes green; session close commits the dirty tree) | the whole tree |
|
|
11
|
+
| manual snapshot | the human | any time, freely | anything |
|
|
12
|
+
|
|
13
|
+
Beyond these, usage (remotes, branches, bundles, whether to rebase) is the user's own; the framework does not regulate it.
|
|
14
|
+
|
|
15
|
+
## The minimal contract
|
|
16
|
+
|
|
17
|
+
1. **Message format**: `type: summary` — the same shape as a wiki/log entry (type from the log enumeration: map / save / query / check / plugin / todo / profile / other; optional `[domain]` tag). Git history becomes the mechanical twin of the log
|
|
18
|
+
2. **Red lines, exactly three**:
|
|
19
|
+
- credentials and session files never enter git
|
|
20
|
+
- adding an ignore must sweep already-tracked matches out of tracking
|
|
21
|
+
- project-domain entities keep their own git — the instance discloses, never hosts
|
|
22
|
+
3. **The recovery entrance**: a check-flagged page → `git log -- <page>` (who changed it) → `git checkout -- <page>` (yesterday's truth back)
|
|
23
|
+
|
|
24
|
+
## Attached audit (read-only)
|
|
25
|
+
|
|
26
|
+
`scripts/check.py` runs on `check` and reports:
|
|
27
|
+
|
|
28
|
+
| Check | Catches |
|
|
29
|
+
|---|---|
|
|
30
|
+
| ignore-rule vs tracked-file residue | files matching an ignore rule that are still tracked (the dsh-router lesson, mechanized) |
|
|
31
|
+
| credential-pattern filenames | `*.ses`, `.env*`, `*cookie*`, `*secret*`, `credentials*` tracked |
|
|
32
|
+
| sensitive faces + remote | a remote is configured while `vault/` / `cuhksz/` faces are tracked — confirm it is private |
|
|
33
|
+
| entity self-git tracked | `projects/*/` carrying their own `.git` yet tracked by the parent |
|
|
34
|
+
|
|
35
|
+
Not a git repository at the root (no `.git`)? The contract is dormant — one info line, no warnings.
|
|
36
|
+
|
|
37
|
+
## One upgrade path, FYI
|
|
38
|
+
|
|
39
|
+
Pure local (the default) → a `git bundle` file copied elsewhere → a private remote. Before any remote, sweep the sensitive faces (vault/ assets, cuhksz course records — anything you would not hand to a stranger); public remotes are out of the question for an instance.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
id: instance-git
|
|
2
|
+
schema_version: 1
|
|
3
|
+
version: 0.1
|
|
4
|
+
depends: []
|
|
5
|
+
updated: 2026-10-13
|
|
6
|
+
attachment: []
|
|
7
|
+
fields: {}
|
|
8
|
+
inject: "Instance git = the snapshot ledger of real use (rollback + time machine), never a collaboration face: three authors — the kernel never touches git (the journal is its ledger), the agent commits at transaction close (type: summary, log-shaped), humans snapshot freely; red lines — credentials/session files never tracked, adding an ignore sweeps tracked matches out, project entities keep their own git (the instance discloses, never hosts); recovery entrance: git log -- <page> → git checkout -- <page>; beyond this contract, usage is the human's own"
|
|
9
|
+
usage:
|
|
10
|
+
- Package transactions: after validate goes green, one commit at close — mechanical + semantic in one snapshot (message `plugin: <verb> <pkg> [v<version>]`); see the plugin command's Transaction close section
|
|
11
|
+
- Session close: commit the dirty tree the session leaves (`type: summary`, optional `[domain]` tag — the same shape as a log entry); no other rhythm is mandated
|
|
12
|
+
- Recovery entrance: a check-flagged page → `git log -- <page>` (who changed it, when) → `git checkout -- <page>` (yesterday's truth back)
|
|
13
|
+
- Upgrade path FYI: pure local (default) → a `git bundle` copied elsewhere → a private remote (sweep sensitive faces first); public remotes are out of the question for an instance
|
|
14
|
+
checks:
|
|
15
|
+
- Mechanical (attached audit, read-only): ignore-rule vs tracked-file residue; credential-pattern filenames tracked; sensitive faces tracked while a remote is configured; projects/*/ self-git repos tracked by the parent — all warnings, the script never writes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""instance-git attached audit: read-only checks of the snapshot-contract red lines.
|
|
4
|
+
|
|
5
|
+
Never writes, never prompts. Four checks:
|
|
6
|
+
1. ignore-rule vs tracked-file residue (files matching an ignore rule yet still tracked)
|
|
7
|
+
2. credential-pattern filenames among tracked files
|
|
8
|
+
3. sensitive faces (vault/, cuhksz/) tracked while a remote is configured
|
|
9
|
+
4. projects/*/ self-git repos tracked by the parent
|
|
10
|
+
Not a git repository at the root -> contract dormant, one info line.
|
|
11
|
+
"""
|
|
12
|
+
import fnmatch
|
|
13
|
+
import os
|
|
14
|
+
import subprocess
|
|
15
|
+
|
|
16
|
+
IGNORE_BASE = ["*.ses", ".env", ".env.*", "*cookie*", "*secret*", "credentials*", "*.pem"]
|
|
17
|
+
SENSITIVE_TOP = {"vault", "cuhksz"}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _git(root, *args):
|
|
21
|
+
return subprocess.run(["git", "-C", root, *args], capture_output=True, text=True, timeout=30)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _ignore_rules(root):
|
|
25
|
+
"""Parse the root .gitignore: non-negated patterns only, trailing slashes stripped."""
|
|
26
|
+
path = os.path.join(root, ".gitignore")
|
|
27
|
+
rules = []
|
|
28
|
+
if not os.path.exists(path):
|
|
29
|
+
return rules
|
|
30
|
+
for line in open(path, encoding="utf-8").read().splitlines():
|
|
31
|
+
line = line.strip()
|
|
32
|
+
if not line or line.startswith("#") or line.startswith("!"):
|
|
33
|
+
continue
|
|
34
|
+
rules.append(line.rstrip("/"))
|
|
35
|
+
return rules
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _matches_rule(rel, rule):
|
|
39
|
+
"""Directory-prefix or fnmatch, mirroring gitignore spirit closely enough for an audit."""
|
|
40
|
+
if fnmatch.fnmatch(rel, rule) or fnmatch.fnmatch(rel, rule + "/*"):
|
|
41
|
+
return True
|
|
42
|
+
parts = rel.split("/")
|
|
43
|
+
for i in range(1, len(parts)):
|
|
44
|
+
if fnmatch.fnmatch("/".join(parts[:i]), rule):
|
|
45
|
+
return True
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def check(ctx):
|
|
50
|
+
issues = []
|
|
51
|
+
root = ctx.root
|
|
52
|
+
if not os.path.exists(os.path.join(root, ".git")):
|
|
53
|
+
issues.append({"level": "info", "message": "instance-git: no .git at the root — snapshot contract dormant"})
|
|
54
|
+
return issues
|
|
55
|
+
tracked = [l for l in _git(root, "ls-files").stdout.splitlines() if l.strip()]
|
|
56
|
+
if not tracked:
|
|
57
|
+
issues.append({"level": "info", "message": "instance-git: nothing tracked — snapshot contract dormant"})
|
|
58
|
+
return issues
|
|
59
|
+
# 1. ignore vs tracked residue
|
|
60
|
+
rules = _ignore_rules(root)
|
|
61
|
+
residue = sorted({rel for rel in tracked for rule in rules if _matches_rule(rel, rule)})
|
|
62
|
+
if residue:
|
|
63
|
+
issues.append({"level": "warning", "message": "instance-git: ignored-but-tracked residue (sweep out: git rm --cached) — " + ", ".join(residue[:6]) + (" …" if len(residue) > 6 else "")})
|
|
64
|
+
# 2. credential patterns
|
|
65
|
+
hits = sorted({rel for rel in tracked for pat in IGNORE_BASE if fnmatch.fnmatch(os.path.basename(rel), pat)})
|
|
66
|
+
if hits:
|
|
67
|
+
issues.append({"level": "warning", "message": "instance-git: credential-pattern files tracked (red line 1) — " + ", ".join(hits[:6])})
|
|
68
|
+
# 3. sensitive faces + remote
|
|
69
|
+
remotes = _git(root, "remote").stdout.split()
|
|
70
|
+
tops = {rel.split("/")[0] for rel in tracked if "/" in rel}
|
|
71
|
+
if remotes and tops & SENSITIVE_TOP:
|
|
72
|
+
issues.append({"level": "warning", "message": f"instance-git: remote(s) configured ({', '.join(remotes)}) while sensitive faces ({', '.join(sorted(tops & SENSITIVE_TOP))}/) are tracked — confirm private; public remotes are out of the question"})
|
|
73
|
+
# 4. entity self-git tracked by the parent (a gitlink shows as bare "projects/<name>", a stray add as "projects/<name>/...")
|
|
74
|
+
pdir = os.path.join(root, "projects")
|
|
75
|
+
if os.path.isdir(pdir):
|
|
76
|
+
for name in sorted(os.listdir(pdir)):
|
|
77
|
+
if os.path.isdir(os.path.join(pdir, name, ".git")):
|
|
78
|
+
leaked = [rel for rel in tracked if rel == f"projects/{name}" or rel.startswith(f"projects/{name}/")]
|
|
79
|
+
if leaked:
|
|
80
|
+
issues.append({"level": "warning", "message": f"instance-git: projects/{name}/ keeps its own git yet {len(leaked)} entr{'y' if len(leaked) == 1 else 'ies'} tracked by the parent (red line 3 — disclose, never host)"})
|
|
81
|
+
return issues
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
import types as _t
|
|
86
|
+
_r = check(_t.SimpleNamespace(root=os.getcwd(), pages=[]))
|
|
87
|
+
if not _r:
|
|
88
|
+
print("instance-git attached audit: all clean")
|
|
89
|
+
for _i in _r:
|
|
90
|
+
print(_i["level"] + ": " + _i["message"])
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# language: output language and writing
|
|
2
|
+
|
|
3
|
+
## Design Overview
|
|
4
|
+
|
|
5
|
+
- **Why it exists**: output-language choice and term translations were previously scattered across a one-line charter clause (AGENTS principle 7) and stray conventions in each domain — nowhere to look up across sessions, translations drifting with the session (today '检索增强生成', tomorrow '检索增强式生成'). This plugin establishes one declaration page: language and writing baselines in writing, the term table mechanically consultable, a rule to follow before the pen touches the page
|
|
6
|
+
- **Position in the family**: a global cross-cutting plugin, declaration-page pattern (structure precedent) — the plugin governs the mechanism, concrete norms are instance configuration landing on the page; division of labor with user-profile — the profile governs dynamic cognition of the individual user (evidence-driven, convergence-style), this page governs the instance-level constant writing regime (declarative, true for all sessions); division of labor with tag — tag governs `tags` field word forms, this page governs body prose
|
|
7
|
+
- **Key rulings**:
|
|
8
|
+
- Absence tolerance: page or key absent = follow the session language (conversation language is the output language); a language norm is an optional enhancement, not a requirement — isomorphic with structure; proper nouns, code, paths, commands, and file names always keeping their original form is the language-neutrality discipline, invariant under configuration
|
|
9
|
+
- Language neutrality (2026-10-04 batch): the framework hard-codes no specific language — output language is entirely instance data (declaration page values or session language); layered ruling: source alignment (materials are in whatever language they are in, e.g. exam language follows course materials) belongs to domain-plugin discipline, reader alignment (what language lectures and solutions use) belongs to this page's canonical keys (the teaching/annotation precedent); 'Chinese-first' demoted to a development-period instance fact (AGENTS principle 7) — when English unification finally comes, project docs change, not the architecture
|
|
10
|
+
- A default baseline, not a mandate: the domain plugins' source-alignment discipline takes precedence over this page; this page never overrides in-domain self-managed language discipline
|
|
11
|
+
- Emergence-based term registry: annotate the original on first occurrence, register only after recurring hits — the email source-archive emergence philosophy, preventing a page-per-term explosion
|
|
12
|
+
- Two-form partitioning: the two frontmatter mappings are the mechanical zone (regenerably maintained), the body distillation section is append-only (translation rationale, usage examples, abandoned renderings)
|
|
13
|
+
- **Rejected alternatives**:
|
|
14
|
+
- Merging into user-profile: observation-convergence and instance configuration are heterogeneous in nature, and the term table is a shared asset, not personal cognition — rejected
|
|
15
|
+
- Writing into AGENTS principle 7: the charter is law, not data, and the term table keeps growing — rejected
|
|
16
|
+
- A page per term (`wiki/terms/`): terms are lookup pieces, not written artifacts; a single-page mapping suffices, with an upgrade path to discuss if it overflows the window — rejected
|
|
17
|
+
- **Mechanism back-references**: declaration-page absence-tolerance precedent structure; emergence precedent the email source archive; block mappings draw on the wiki frontmatter minimal YAML subset; usage lands on the save command via usage_routes — the first application of the source-side routing mechanism, 2026-10-04
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
- `wiki/language.md` — the writing declaration page, type: language. Frontmatter `language` block mapping = canonical key → one-sentence rule (open vocabulary, self-extended by instances); `terms` block mapping = term original → unified translation (single-line value); the body `## Distillations` section holds long notes, append-only
|
|
22
|
+
- Zero commands: reading is consuming (hung on save via usage_routes), writing goes through conversational editing; v0.1 sets no registration command
|
|
23
|
+
|
|
24
|
+
## Invariants
|
|
25
|
+
|
|
26
|
+
- This page is a default baseline, not a mandate: the domain plugins' source-alignment discipline takes precedence; this page never touches in-domain self-managed discipline
|
|
27
|
+
- Three boundary non-involvements: forms of address and personal dynamic preferences belong to user-profile, tag word forms belong to tag, in-domain term tables (the bb courseware glossary) are self-managed in-domain and never lifted up
|
|
28
|
+
- Canonical keys are an open vocabulary, not enumerated by the architecture (principle 3)
|
|
29
|
+
- Emergence-based term registration; direct human edits are legitimate, the agent never overwrites human-defined entries (objections reported)
|
|
30
|
+
- Page absence = a legal state (follow the session language); no error, no proxy-creation
|
|
31
|
+
- The framework presets no specific language: output language is entirely instance data (declaration page values or session language); Chinese is merely a development-period fact of this project
|
|
32
|
+
|
|
33
|
+
## Changelog
|
|
34
|
+
|
|
35
|
+
- 0.5 (2026-10-23): restore the register discipline in manifest usage routed to save and align the field description; generated command projections now retain it across rebuilds (issue #26).
|
|
36
|
+
- 0.4 (2026-10-07): register key — the `register` canonical key governs the audience reading level (program default first/second-year undergraduates, AGENTS principle 12), consumed by bb-teach (teaching) / bb-quiz (annotation) / save (PR #23, renumbered past master's 0.3 compression)
|
|
37
|
+
- 0.3 (2026-10-06) inject line compressed to pointer density (issue #12 layer discipline) — procedural detail lives in usage / PLUGIN.md / skills- 0.2 2026-10-04: language neutralization — the absence fallback changed from 'Chinese-first' to 'follow the session language', Chinese demoted to a development-period instance fact; layered ruling: source alignment belongs to domain plugins, reader alignment to this page's canonical keys (the teaching/annotation precedent); same batch as bb-teach/bb-quiz v0.4
|
|
38
|
+
- 0.1 2026-10-04: established — the writing declaration page with two-form partitioning (language/terms block mappings + distillation section), absence tolerance, default-baseline-not-mandate, emergence-based term registry; usage_routes landing on save, first application; depends wiki
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
id: language
|
|
2
|
+
schema_version: 1
|
|
3
|
+
version: 0.5
|
|
4
|
+
depends:
|
|
5
|
+
- wiki
|
|
6
|
+
updated: 2026-10-23
|
|
7
|
+
attachment:
|
|
8
|
+
- "wiki/language.md — the writing declaration page (output-language and writing-baseline ontology: frontmatter language/terms block mappings + body distillation section)"
|
|
9
|
+
- "scripts/check.py — attached audit (territory placement / terms empty values / declaration page presence)"
|
|
10
|
+
fields:
|
|
11
|
+
language: block mapping (declaration page) — canonical key → one-sentence rule (open vocabulary, e.g. default/teaching/annotation/register); output language and writing baseline — register = the writing register (audience reading level, default first/second-year undergraduates), consumed by bb-teach, bb-quiz and save
|
|
12
|
+
terms: block mapping (declaration page) — term original → unified translation (single-line value); the cross-session translation-consistency lookup piece
|
|
13
|
+
usage_routes:
|
|
14
|
+
- save
|
|
15
|
+
inject: "Writing declaration page wiki/language.md (type: language): language block mapping = key → one-sentence rule (consumer precedents: teaching bb-teach, annotation bb-quiz, register = audience reading level, program default first/second-year undergraduates per AGENTS principle 12) + terms = original → unified translation; body distillation notes append-only; the agent reads it before producing written artifacts; page or key absent → session language (proper nouns/code/paths keep original); default baseline not a mandate — domain source-alignment disciplines take precedence; terms emergence-based (register after recurring hits); direct human edits legitimate"
|
|
16
|
+
usage:
|
|
17
|
+
- Register discipline: follow the language.register audience reading level when writing saved artifacts; absent register uses the program baseline (first/second-year undergraduates), while explicit user audience requirements and domain source-alignment rules take precedence
|
|
18
|
+
- When to read: read the declaration page before writing any artifact (wiki pages, documents, lecture distillates); page or key absent = follow the session language (conversation language is the output language); proper nouns, code, paths, commands, and file names always keep their original form (language neutrality, invariant under configuration) — the framework presets no specific language; Chinese is merely an instance fact of this project's development period (AGENTS principle 7)
|
|
19
|
+
- Term discipline: when writing hits a term, consult the `terms` mapping first — if tabled, follow the table; otherwise annotate the original in parentheses on first occurrence (e.g. '检索增强生成 (RAG)'), registering only after recurring hits (emergence-based); a registered entry = a single line original→translation, long notes (translation rationale, usage examples) go to the body distillation section
|
|
20
|
+
- Layering: a default baseline, not a mandate — the domain plugins' source-alignment discipline (exam language follows samples/course materials) takes precedence over this page; tag word forms belong to tag, forms of address and personal dynamic preferences belong to user-profile, in-domain term tables (the bb courseware glossary) are self-managed within the domain — none of these are restated on this page
|
|
21
|
+
- Maintenance: the `language` mapping is human-defined first, the agent may propose; the `terms` mapping may be registered by the agent but never overwrites human-defined entries (objections reported); the body distillation section is append-only
|
|
22
|
+
checks:
|
|
23
|
+
- Mechanical (attached-audit script `scripts/check.py`, executed discovery-style by audit): type: language landing outside `wiki/language.md` → error; terms empty values → warning; declaration page absent → info (session-language fallback tolerated)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""language plugin attached audit: declaration page existence, territory misplacement, empty terms values.
|
|
4
|
+
|
|
5
|
+
A missing declaration page = tolerated default posture (legal, info); an empty terms value is a registry defect.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def check(ctx):
|
|
10
|
+
issues = []
|
|
11
|
+
decl = False
|
|
12
|
+
for rel, fm, _body in ctx.pages:
|
|
13
|
+
if fm.get("type") == "language" and rel != "language.md":
|
|
14
|
+
issues.append({"level": "error", "message": f"{rel}: type: language outside the declaration page (territory misplacement)"})
|
|
15
|
+
if rel == "language.md":
|
|
16
|
+
decl = True
|
|
17
|
+
terms = fm.get("terms") or {}
|
|
18
|
+
for k, v in terms.items():
|
|
19
|
+
if not str(v).strip():
|
|
20
|
+
issues.append({"level": "warning", "message": f"terms empty value: {k} (translated name missing; fill in or remove)"})
|
|
21
|
+
if not decl:
|
|
22
|
+
issues.append({"level": "info", "message": "no language declaration page (default posture tolerated)"})
|
|
23
|
+
return issues
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# link: Link Layer
|
|
2
|
+
|
|
3
|
+
## Design Summary
|
|
4
|
+
|
|
5
|
+
- **Why it exists**: wiki's ontological structure — reference relations between pages; links are where the value of knowledge lies, thinking happens where things collide. A field plugin: owns the link syntax and the related and aliases fields, and hosts the graph-property health checks — broken links and orphans
|
|
6
|
+
- **Key rulings**:
|
|
7
|
+
- Full names, not titles: `[[page full name]]` = the relative path within wiki/ minus one .md — an extension of "the path is the origin" into the link layer; truncated references forbidden; same-name ambiguity resolved by path, disambiguation never by guessing
|
|
8
|
+
- Broken links are not defects: a broken link may be knowledge not yet written down, TODO placeholders are a normal state; warning not error; when unsure, better to leave a TODO than to guess
|
|
9
|
+
- Inbound sources for orphan detection count only concept pages: derived pages and tmp don't count — otherwise the index links everything and orphans never trigger; mechanical-registration territory pages drop to info, registration being the norm; notes knowledge pages stay warning
|
|
10
|
+
- The syntax conventions must enter the injection line: when the conventions lived only in the attached-audit source, a zero-prior agent was bound to guess wrong, proven in 0.8 — a lesson specimen of disclosure completeness
|
|
11
|
+
|
|
12
|
+
## Structure
|
|
13
|
+
|
|
14
|
+
- `related` field: YAML list; full names of pages related to this page's topic, i.e. wikilinks
|
|
15
|
+
- `aliases` field: YAML list; this page's aliases and short names, for link resolution and retrieval
|
|
16
|
+
- wikilink syntax conventions: `[[page full name]]`. **Full name = the page file's relative path within wiki/ minus the trailing .md**, e.g. `notes/X`; an md asset's proxy file is `<name>.md.md`, its full name `vault/<name>.md`, only one removed. Truncated partial references are forbidden; long compound names must be written in full; on same-name ambiguity, include the path
|
|
17
|
+
|
|
18
|
+
## Invariants
|
|
19
|
+
|
|
20
|
+
- Link targets must resolve: the full name hits a page, or hits some page's aliases
|
|
21
|
+
- Broken links are not silent but not treated as defects: a broken link may be knowledge not yet written down, TODO placeholders are a normal shape; when unsure of the target, better to leave a TODO than to guess
|
|
22
|
+
- Orphan detection is a graph property: only a page with no inbound links and no related references is an orphan; **inbound sources count only concept pages** — index, hot, log, tags and other derived pages, the archive/ subtree, and the tmp/ temporary zone with its draft broken-link exemption — none count as link sources, otherwise the index links everything and orphans never trigger. Territory-value pages — registry territory values except session, i.e. source, lark, calendar, structure, todo, profile, project, tmp, the mechanical-registration kinds — having no inbound links yet is the registration norm, downgraded to info; notes knowledge pages stay warning. hot is the only derived page with hand-written links: its broken links are checked but it is not an inbound-link source
|
|
23
|
+
|
|
24
|
+
## Changelog
|
|
25
|
+
|
|
26
|
+
- 0.14 2026-09-23: added the wiki dependency edge — inner-side plugins attach to wiki, aligning with the domain 0.1 declaration; omitted from the 2026-09-22 domain-ization batch
|
|
27
|
+
- 0.13 2026-09-19: _concept excluded wiki/tmp/ — the temporary zone is not a link source and is exempt from graph checks; a draft's broken link = not yet written down, closed upon promotion
|
|
28
|
+
- 0.12 2026-09-19: the orphan info set changed to dynamically reading registry type.values except session — new territory types covered automatically, the hardcoded set retired, prerequisite for project's establishment
|
|
29
|
+
- 0.11 2026-09-19: the orphan info set extended to all territory values except session — calendar smoke testing confirmed false-positive warnings on calendar.md and month pages; the judgment aligned with registry territory values, no more squeezing toothpaste type by type
|
|
30
|
+
- 0.10 2026-09-19: the orphan-downgrade criterion changed from path-based to type-based, source and lark treated alike — the lark-family landing smoke test confirmed: new pointer pages falsely warned en masse
|
|
31
|
+
- 0.9 2026-09-13: attached audit gained the hot hand-written broken-link scan; orphan grading — proxy pages dropped to info; expert review: warning inflation; the injection line gained the md-asset full-name example
|
|
32
|
+
- 0.8 2026-09-13: the full-name definition entered the injection line and Structure — test-repo walkthrough finding: the conventions lived only in the attached-audit source, a zero-prior agent was bound to guess wrong
|
|
33
|
+
- 0.7 2026-09-13: injection source moved to the manifest — removed Checks, Inject, Attachments sections, md returned to pure documentation
|
|
34
|
+
- 0.6 2026-09-12: manifest dropped layer — layering abolished: injection order changed to dependency topology plus alphabetical order, direction checks removed
|
|
35
|
+
- 0.5 2026-09-12: identifiers anglicized — section headers, attached-audit contract keys, type enums, pipeline call parameters
|
|
36
|
+
- 0.4 2026-09-11: mechanical items absorbed into the attached-audit script — broken links, garbled text, orphans; new alias-ambiguity as error, and one-way related as info; the hub list kept as a semantic item
|
|
37
|
+
- 0.3 2026-09-10: broken links downgraded to warning — knowledge not yet written down, not a defect; the orphan-detection scope written down, derived pages don't count as inbound sources
|
|
38
|
+
- 0.2 2026-09-10: manifest gained layer: field — layering established: field layer, zero dependencies
|
|
39
|
+
- 0.1 2026-09-09: newly established — link syntax and the related and aliases fields; the broken-link check converted from the original lint; the orphan check moved over from notes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: link
|
|
2
|
+
schema_version: 1
|
|
3
|
+
version: 0.14
|
|
4
|
+
depends:
|
|
5
|
+
- wiki
|
|
6
|
+
updated: 2026-10-06
|
|
7
|
+
attachment:
|
|
8
|
+
- "Cross-cutting plugin, no files of its own: owns the link-syntax conventions and the related / aliases fields"
|
|
9
|
+
- "scripts/check.py — attached audit (broken links / garbled text / alias ambiguity / orphans / one-way related)"
|
|
10
|
+
fields:
|
|
11
|
+
related: "YAML list, full-name wikilinks of related pages"
|
|
12
|
+
aliases: "YAML list, aliases/short names, for link resolution"
|
|
13
|
+
inject: "Link syntax `[[page full name]]` — full name = the relative path within wiki/ minus the trailing .md (e.g. `notes/X`, pdf asset proxy `vault/a.pdf`, md asset proxy `vault/<name>.md`, only one removed); truncated references forbidden, same-name ambiguity resolved by path; fields `related` / `aliases`; broken link = warning (not yet written down), orphan (no inbound links and no references, derived pages don't count as sources) = warning for notes knowledge pages, info for territory-value registration pages (dynamically reads registry type.values, except session)"
|
|
14
|
+
checks:
|
|
15
|
+
- Mechanical items (attached-audit script `scripts/check.py`, executed on audit discovery): broken link (target is neither a page full name nor any page's aliases) → warning; hand-written broken link in hot → warning (not counted as an inbound-link source); garbled link (target contains the U+FFFD replacement character, including related items) → error; alias ambiguity (two pages declaring the same aliases, resolution indeterminate) → error; orphan page (no inbound links and no related references, inbound sources counting concept pages only) → warning for notes knowledge pages, info for territory-value registration pages (registry territory values except session, having no inbound links yet being the registration norm); one-way related (A lists B while B does not list back) → info
|
|
16
|
+
- Semantic items (check command): inbound-link density top list → info item (evidence for hub emergence, no alert)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""link plugin attached audit: mechanical items (broken links / garbled text / alias ambiguity / orphans / one-way related).
|
|
4
|
+
|
|
5
|
+
The in-link density top list (evidence for hub emergence) is an analytical item, under the check command's semantic items.
|
|
6
|
+
Concept-page determination is isomorphic to the pipeline: reserved names (index/log), wiki-root derived pages (hot/tags),
|
|
7
|
+
the archive/ subtree and the tmp/ temporary zone are not link sources and are exempt from graph checks (derived pages link
|
|
8
|
+
to everything, otherwise orphans would never trigger; a broken link in a draft = not yet written down, closed upon promotion).
|
|
9
|
+
hot is the only derived page with handwritten links: its broken links are checked, but it is not an in-link source and stays
|
|
10
|
+
out of the orphan graph.
|
|
11
|
+
Territory-value pages (registry type.values except session — the mechanical-registry kind) having no in-links yet is the
|
|
12
|
+
registry norm: orphans are downgraded to info level there, while native pages (notes knowledge pages) stay warning. The
|
|
13
|
+
territory value set is read dynamically from registry, so new territory types are covered automatically — no per-type upkeep.
|
|
14
|
+
"""
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
WIKILINK_RE = re.compile(r"\[\[([^\]|]+)(?:\|[^\]]*)?\]\]")
|
|
19
|
+
RESERVED_NAMES = {"index.md", "log.md"}
|
|
20
|
+
ROOT_DERIVED = {"hot.md", "tags.md"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _as_list(v):
|
|
24
|
+
if v is None:
|
|
25
|
+
return []
|
|
26
|
+
return v if isinstance(v, list) else [v]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _name_of(rel):
|
|
30
|
+
return rel[:-3] if rel.endswith(".md") else rel
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _concept(rel):
|
|
34
|
+
d, _, fn = rel.rpartition("/")
|
|
35
|
+
if fn in RESERVED_NAMES:
|
|
36
|
+
return False
|
|
37
|
+
if d == "" and fn in ROOT_DERIVED:
|
|
38
|
+
return False
|
|
39
|
+
if rel.startswith("tmp/") or d.startswith("tmp/") or d == "tmp":
|
|
40
|
+
return False # temporary zone: not a link source, exempt from graph checks (draft broken-link exemption)
|
|
41
|
+
return not (rel.startswith("archive/") or d.startswith("archive/") or d == "archive")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _target(text):
|
|
45
|
+
"""related item or wikilink text → target full name (tolerates both [[x|display]] and bare-name forms)."""
|
|
46
|
+
return str(text).strip().lstrip("[").split("|")[0].strip().rstrip("]")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
TERRITORY_LABELS = {"source": "proxy page", "lark": "pointer page", "calendar": "time page",
|
|
50
|
+
"structure": "declaration page", "todo": "delegation page", "profile": "profile page", "project": "project page"}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _territory_types(root):
|
|
54
|
+
"""Read registry type.values (closed set of territory values) dynamically; session excluded (knowledge backbone, orphan hint is meaningful there)."""
|
|
55
|
+
try:
|
|
56
|
+
text = open(os.path.join(root, ".meta", "protocol", "registry.yaml"), encoding="utf-8").read()
|
|
57
|
+
m = re.search(r"values: \[([^\]]+)\]", text)
|
|
58
|
+
return {v.strip() for v in m.group(1).split(",") if v.strip()} - {"session"}
|
|
59
|
+
except OSError:
|
|
60
|
+
return set(TERRITORY_LABELS)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def check(ctx):
|
|
64
|
+
issues = []
|
|
65
|
+
pages = list(ctx.pages)
|
|
66
|
+
names = {_name_of(rel) for rel, _fm, _b in pages}
|
|
67
|
+
# alias table and ambiguity (error: makes [[alias]] resolution indeterminate)
|
|
68
|
+
alias_map = {}
|
|
69
|
+
for rel, fm, _b in pages:
|
|
70
|
+
for a in _as_list(fm.get("aliases")):
|
|
71
|
+
alias_map.setdefault(str(a).strip(), set()).add(_name_of(rel))
|
|
72
|
+
for a, owners in sorted(alias_map.items()):
|
|
73
|
+
if len(owners) > 1:
|
|
74
|
+
issues.append({"level": "error", "message": f"alias {a!r} ambiguous: {', '.join(sorted(owners))} — resolution indeterminate, needs adjudication"})
|
|
75
|
+
referenced, related_map = set(), {}
|
|
76
|
+
for rel, fm, body in pages:
|
|
77
|
+
if not _concept(rel):
|
|
78
|
+
continue
|
|
79
|
+
src = _name_of(rel)
|
|
80
|
+
for m in WIKILINK_RE.finditer(body):
|
|
81
|
+
t = m.group(1).strip()
|
|
82
|
+
if "\ufffd" in t:
|
|
83
|
+
issues.append({"level": "error", "message": f"{rel}: garbled link [[{m.group(1)}]]"})
|
|
84
|
+
elif t not in names and t not in alias_map:
|
|
85
|
+
issues.append({"level": "warning", "message": f"{rel}: broken link [[{t}]] (neither a page full name nor any aliases)"})
|
|
86
|
+
referenced.add(t)
|
|
87
|
+
rels = [_target(r) for r in _as_list(fm.get("related"))]
|
|
88
|
+
related_map[src] = {t for t in rels if t}
|
|
89
|
+
for t in rels:
|
|
90
|
+
if "\ufffd" in t:
|
|
91
|
+
issues.append({"level": "error", "message": f"{rel}: related garbled item {t!r}"})
|
|
92
|
+
elif t not in names and t not in alias_map:
|
|
93
|
+
issues.append({"level": "warning", "message": f"{rel}: related broken link [[{t}]]"})
|
|
94
|
+
referenced.add(t)
|
|
95
|
+
# hot: handwritten-link surface (broken links checked; not an in-link source, out of the orphan graph)
|
|
96
|
+
for rel, _fm, body in pages:
|
|
97
|
+
if _name_of(rel) != "hot":
|
|
98
|
+
continue
|
|
99
|
+
for m in WIKILINK_RE.finditer(body):
|
|
100
|
+
t = m.group(1).strip()
|
|
101
|
+
if "\ufffd" in t:
|
|
102
|
+
issues.append({"level": "error", "message": f"{rel}: garbled link [[{m.group(1)}]]"})
|
|
103
|
+
elif t not in names and t not in alias_map:
|
|
104
|
+
issues.append({"level": "warning", "message": f"{rel}: hot broken link [[{t}]] (handwritten summary link no longer valid)"})
|
|
105
|
+
# orphans (no in-links and no related references; sources count concept pages only; territory-value registry pages downgraded to info)
|
|
106
|
+
territory = _territory_types(ctx.root)
|
|
107
|
+
for rel, fm, _b in pages:
|
|
108
|
+
if not _concept(rel):
|
|
109
|
+
continue
|
|
110
|
+
if _name_of(rel) not in referenced:
|
|
111
|
+
ptype = fm.get("type")
|
|
112
|
+
if ptype in territory:
|
|
113
|
+
label = TERRITORY_LABELS.get(ptype, "territory page")
|
|
114
|
+
issues.append({"level": "info", "message": f"{rel}: {label} has no in-links yet (registry norm, not an alert)"})
|
|
115
|
+
else:
|
|
116
|
+
issues.append({"level": "warning", "message": f"{rel}: orphan page (no in-links and no related references)"})
|
|
117
|
+
# related one-way (informational: asymmetry hint, not an error)
|
|
118
|
+
for src, rels in sorted(related_map.items()):
|
|
119
|
+
for t in sorted(rels):
|
|
120
|
+
if t in related_map and src not in related_map[t]:
|
|
121
|
+
issues.append({"level": "info", "message": f"{src}: related one-way (lists [[{t}]], other side does not list back)"})
|
|
122
|
+
return issues
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# log: Run Log
|
|
2
|
+
|
|
3
|
+
## Design Summary
|
|
4
|
+
|
|
5
|
+
- **Why it exists**: the repository's operation journal — what was done to the repository and when. Opposite of hot: hot is the present that gets evicted, log is append-only never-deleted history. The mandatory bridge guarantees that anything any domain produces always leaves a trace — an instance of the topological-completeness invariant, see Section 5 of mechanics
|
|
6
|
+
- **Key rulings**:
|
|
7
|
+
- Entries are append-only and never rewritten, prepended at the top: modifying a historical entry = error; archival moves don't change a single character of an entry
|
|
8
|
+
- Rolling window rather than an infinite ledger: the main file holds at most 100 entries; overflow is mechanically archived, grouped by entry month, to `wiki/archive/<month>/log.md` — freshness and full history both served
|
|
9
|
+
- Writing is mechanized: via `pipeline.py log`; capacity checks and archiving are performed by the script; the authoritative source of parameters is the script source
|
|
10
|
+
- Domain tags, since 0.15: in-domain transaction entries carry `[domain]`; domainless transactions omit it — the journal becomes searchable by domain
|
|
11
|
+
|
|
12
|
+
## Structure
|
|
13
|
+
|
|
14
|
+
- Single file `wiki/log.md`; entries prepended at the top, newest on top
|
|
15
|
+
- Entry format: `- YYYY-MM-DD <type> [<domain>]: one-sentence summary`, may contain wikilinks; the domain tag is omissible, i.e. domainless transactions
|
|
16
|
+
- Type enum: map, save, query, check, plugin, todo, other; instances may extend
|
|
17
|
+
|
|
18
|
+
## Bridge: mandatory
|
|
19
|
+
|
|
20
|
+
Recording bridge: the operation-journal home shared by all domain plugins across domains, constitution principle 11. Attachment cardinality **mandatory** — all domain bases must depend on this plugin; kernel validate verifies completeness; the topology guarantees: anything a domain produces leaves a trace. Format authority lives in this plugin's usage, projected to the consumption sites, per the division-of-labor ruling. This section details: in-domain transaction entries carry the domain tag `[domain]`, e.g. `[bb]`; domainless transactions omit it; written via `pipeline.py log --domain`.
|
|
21
|
+
|
|
22
|
+
## Invariants
|
|
23
|
+
|
|
24
|
+
- Entries are append-only and never rewritten — the object of "append-only, never delete" is entry content, not the file's physical location; modifying a historical entry = error
|
|
25
|
+
- Every entry must carry a date, accurate to the day
|
|
26
|
+
- Archival moves don't change a single character of entry content
|
|
27
|
+
|
|
28
|
+
## Rolling
|
|
29
|
+
|
|
30
|
+
The main file is a rolling window, not an infinite ledger:
|
|
31
|
+
|
|
32
|
+
- Window of at most 100 entries, about 14k characters; the mechanical authoritative source is `pipeline.py`, this section is the semantic description
|
|
33
|
+
- On overflow the oldest segment is automatically moved, grouped by entry month, into `wiki/archive/YYYY-MM/log.md`; entry content unchanged by a single character, only moved. The archive file is the reserved name log.md of its directory — reserved-name files are not concept pages, naturally exempt from frontmatter
|
|
34
|
+
- The archive directory falls into the immutable zone; compression and consolidation still require human confirmation, see constitution principle 5
|
|
35
|
+
|
|
36
|
+
## Config
|
|
37
|
+
|
|
38
|
+
```yaml config
|
|
39
|
+
log.max_entries: 100 # main-file rolling window, entries
|
|
40
|
+
log.max_chars: 14000 # window character cap, approximate
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Changelog
|
|
44
|
+
|
|
45
|
+
- 0.15 2026-10-02: global-domain batch one — established the mandatory bridge, i.e. the recording bridge; domain bases must depend on it, kernel verifies completeness; entries gained the omissible `[domain]` tag; pipeline log gained the `--domain` parameter
|
|
46
|
+
- 0.13 2026-09-23: added the wiki dependency edge — inner-side plugins attach to wiki, aligning with the domain 0.1 declaration; omitted from the 2026-09-22 domain-ization batch
|
|
47
|
+
- 0.12 2026-09-14: type value set extended with todo — the home of the todo plugin's task-settlement events; history goes to log, the todo page keeps only the active working set
|
|
48
|
+
- 0.11 2026-09-13: usage's type parameter gained its value-set source — the log block of the AGENTS injection region, removing cross-block guess points
|
|
49
|
+
- 0.10 2026-09-13: injection source moved to the manifest — removed Checks, Usage, Inject, Attachments sections, md returned to pure documentation
|
|
50
|
+
- 0.9 2026-09-13: established the "Usage" section — the write-side contract is projected by the command's injection region, a single text source
|
|
51
|
+
- 0.8 2026-09-12: manifest dropped layer — layering abolished: injection order changed to dependency topology plus alphabetical order, direction checks removed
|
|
52
|
+
- 0.7 2026-09-12: identifiers anglicized — section headers, attached-audit contract keys, type enums, pipeline call parameters
|
|
53
|
+
- 0.6 2026-09-11: the undated-entry check moved into the attached-audit script, main file and archive checked alike; the historical-modification audit kept as a semantic item, verifiable via git
|
|
54
|
+
- 0.5 2026-09-10: writing mechanized — via pipeline.py log; capacity checks and archiving performed by the script; the authoritative source of parameters moved to the script source
|
|
55
|
+
- 0.4 2026-09-10: archive path rerouted to `wiki/archive/YYYY-MM/log.md` — grouped by entry month; reserved names exempt from frontmatter
|
|
56
|
+
- 0.3 2026-09-10: manifest gained layer: derived — layering established: derived layer, zero dependencies
|
|
57
|
+
- 0.2 2026-09-09: the rolling-archive mechanism written down, window of 100 entries; the type enum gained "query"; parameters calibrated against the original repository's measurements, averaging 139 characters per entry
|
|
58
|
+
- 0.1 2026-09-08: converted from the original wiki log rules
|