docs: add CONVENTIONS_v1.md; tools: add publish-rename.sh
This commit is contained in:
parent
d813b101d8
commit
ec2fd29954
|
@ -0,0 +1,117 @@
|
||||||
|
# ipfs.kane-il.us — Content & Path Conventions (v1)
|
||||||
|
|
||||||
|
## 0. Scope
|
||||||
|
- Public-only repository for IPFS gateway publishing.
|
||||||
|
- Text formats only: `*.md`, `*.txt`, `*.jsonl`.
|
||||||
|
- No binaries (pdf, images, video, archives) — describe them in markdown for accessibility.
|
||||||
|
- All timestamps are **UTC**.
|
||||||
|
|
||||||
|
## 1. Top-level categories (roots)
|
||||||
|
```
|
||||||
|
/legal
|
||||||
|
/civics
|
||||||
|
/naics
|
||||||
|
/barter
|
||||||
|
/w3pbs
|
||||||
|
/onet
|
||||||
|
/_log # public minimal publish log
|
||||||
|
```
|
||||||
|
- Root-level design documents (e.g. CONVENTIONS, README, checklists) may be placed directly at repo root.
|
||||||
|
- To avoid collisions, these use versioned filenames like `CONVENTIONS_v1.md`, `CONVENTIONS_v2.md`.
|
||||||
|
|
||||||
|
## 2. Naming rules (files & dirs)
|
||||||
|
- **Lowercase**, **ASCII**, **underscores** for word separators.
|
||||||
|
- Filenames must start with **digits-only ISO UTC timestamp**:
|
||||||
|
- `YYYYMMDDThhmmssZ_<slug>.md`
|
||||||
|
- Exception: root design documents, which follow `<NAME>_vN.md`.
|
||||||
|
|
||||||
|
## 3. Immutability
|
||||||
|
- No “latest” aliases, no mutable pointers.
|
||||||
|
- No per-directory index files; rely on gateway directory listing.
|
||||||
|
- Every publish is additive; removals are exceptional (court/admin error).
|
||||||
|
|
||||||
|
## 4. LEGAL
|
||||||
|
### 4.1 research (jurisdiction-first, typed entities)
|
||||||
|
Path template:
|
||||||
|
```
|
||||||
|
/legal/research/{scope}/{segment}/{subject}/...
|
||||||
|
```
|
||||||
|
- `scope ∈ {county|town|township|individual|organization}`
|
||||||
|
- If `scope=organization` or `individual`, use typed entity layering:
|
||||||
|
```
|
||||||
|
/legal/research/{scope}/{kind}/{name}/{subject}/...
|
||||||
|
kind ∈ {governance|economic|domestic|technical|infrastructure|virtual}
|
||||||
|
```
|
||||||
|
- Otherwise:
|
||||||
|
```
|
||||||
|
/legal/research/{scope}/{jurisdiction_name}/{subject}/...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 court → cases
|
||||||
|
Path template:
|
||||||
|
```
|
||||||
|
/legal/court/{court_id}/{case_id}/(filings|evidence|orders|docket|notes|transcripts|exhibits)/
|
||||||
|
```
|
||||||
|
- `court_id` will follow Kane County authoritative naming.
|
||||||
|
- `case_id` sanitized to lowercase/underscores; original case identifier stored inside the markdown.
|
||||||
|
|
||||||
|
## 5. Other categories
|
||||||
|
- **civics/**: governance, policy, licenses, standards → plain markdown.
|
||||||
|
- **naics/**: subdirectories by NAICS code.
|
||||||
|
- **barter/**: web2py assets and git-oriented text/config (no binaries).
|
||||||
|
- **w3pbs/**: mailing lists, streaming, chat, forums; html/gemini as text or markdown summaries.
|
||||||
|
- **onet/**: O*NET taxonomy (latest), directories from O*NET categories.
|
||||||
|
|
||||||
|
## 6. Publish log (public, minimal)
|
||||||
|
- Location: `/_log/publish.jsonl`
|
||||||
|
- One JSON object per line with fields:
|
||||||
|
- `ts` (UTC string),
|
||||||
|
- `event` (e.g. `"publish"`),
|
||||||
|
- `man` (manifest path),
|
||||||
|
- `sig` (signature path),
|
||||||
|
- `signer` (e.g. publisher@ipfs.kane-il.us),
|
||||||
|
- `ns` (signature namespace, canonical = `ipfspublish`),
|
||||||
|
- `repo` (e.g. `w3pbs/ipfs.kane-il.us`),
|
||||||
|
- `commit` (git hash).
|
||||||
|
- Past entries retained forever.
|
||||||
|
- Fields like `manifest_cid`, `paths`, and `tag` may be added in future expansions.
|
||||||
|
|
||||||
|
## 7. Signatures
|
||||||
|
- All manifests are signed with **SSH-sig** using the deploy key.
|
||||||
|
- Canonical namespace: **`ipfspublish`**.
|
||||||
|
- Verification uses `allowed_signers` file with principal `publisher@ipfs.kane-il.us`.
|
||||||
|
|
||||||
|
## 8. Guardrails (deny)
|
||||||
|
- Private/staging content never enters this repo.
|
||||||
|
- Deny common tool dirs: `.git/`, `.github/`, `.vscode/`, caches, temp files.
|
||||||
|
- Deny binaries by extension (see `.gitignore`).
|
||||||
|
|
||||||
|
## 9. Accessibility
|
||||||
|
- For any non-text source (e.g., a PDF elsewhere), store a **descriptive .md**: what it is, where it exists, date, and an accessible summary.
|
||||||
|
|
||||||
|
## 10. Examples
|
||||||
|
```
|
||||||
|
/legal/research/county/kane/zoning/20250828T140000Z_buffer_requirements.md
|
||||||
|
/legal/court/il_kane_circuit/23_ch_000123/filings/20250828T142500Z_plaintiff_motion.md
|
||||||
|
/naics/541611/20250828T150000Z_procurement_policy.md
|
||||||
|
/onet/architecture_and_engineering/20250828T160000Z_job_function_overview.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. Operator Utilities
|
||||||
|
* **Primary interface:** Operators are expected to use **Midnight Commander (`mc`)** as the daily driver for browsing, staging, and publishing files.
|
||||||
|
* **Backup scripts:** Deterministic shell scripts are maintained under `/tools/`.
|
||||||
|
|
||||||
|
* Purpose: fallback when `mc` is unavailable, misconfigured, or fails mid-operation.
|
||||||
|
* Behavior: scripts normalize filenames, run publish steps, and include `--dry-run` modes.
|
||||||
|
* Example:
|
||||||
|
|
||||||
|
* `publish-rename` → normalize names to required conventions.
|
||||||
|
* `publish-doc` (planned) → chain the full publishing flow.
|
||||||
|
* **mc integration:** These scripts may also be bound to the mc user menu (`~/.config/mc/mc.menu`) so that the same deterministic actions can be triggered via `F2`.
|
||||||
|
* **Automation:** Because they are standalone, scripts can also be called in batch or from other automation tooling if desired.
|
||||||
|
|
||||||
|
**Principle:**
|
||||||
|
|
||||||
|
* **mc = primary operator UX** (interactive, user-friendly).
|
||||||
|
* **scripts = deterministic backup + automation hooks**.
|
||||||
|
* Both are valid, auditable, and kept in sync with these conventions.
|
|
@ -0,0 +1,143 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# publish-rename: normalize filenames for ipfs.kane-il.us publishing
|
||||||
|
# - Category content: /<category>/YYYYMMDDThhmmssZ_<slug>.<ext>
|
||||||
|
# - Root design docs: /<NAME>_vN.md
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
VERSION="1.0"
|
||||||
|
|
||||||
|
# ---------- config ----------
|
||||||
|
ALLOWED_EXT_REGEX='^(md|txt|jsonl)$'
|
||||||
|
UTC_FMT='+%Y%m%dT%H%M%SZ'
|
||||||
|
|
||||||
|
# ---------- helpers ----------
|
||||||
|
err(){ printf 'error: %s\n' "$*" >&2; exit 1; }
|
||||||
|
note(){ printf '%s\n' "$*" >&2; }
|
||||||
|
ts(){ date -u "${UTC_FMT}"; }
|
||||||
|
|
||||||
|
slugify(){
|
||||||
|
# lower, spaces->_, remove disallowed chars, collapse repeats, trim underscores
|
||||||
|
printf '%s' "$1" \
|
||||||
|
| tr '[:upper:] ' '[:lower:]_' \
|
||||||
|
| sed -E 's/[^a-z0-9_]+/_/g; s/_+/_/g; s/^_+//; s/_+$//'
|
||||||
|
}
|
||||||
|
|
||||||
|
print_man(){
|
||||||
|
cat <<'MAN'
|
||||||
|
NAME
|
||||||
|
publish-rename — normalize filenames for publishing to ipfs.kane-il.us
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
publish-rename category [--dry-run] <category> <file> <slug>
|
||||||
|
publish-rename root [--dry-run] --version <N> <file> <name>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Renames content into the repository’s required shapes:
|
||||||
|
|
||||||
|
1) CATEGORY MODE
|
||||||
|
Target: /<category>/YYYYMMDDThhmmssZ_<slug>.<ext>
|
||||||
|
- <category> must be one of: legal, civics, naics, barter, w3pbs, onet
|
||||||
|
- <ext> must be md|txt|jsonl (unchanged from <file>)
|
||||||
|
- <slug> is normalized (lowercase, underscores, ASCII)
|
||||||
|
|
||||||
|
2) ROOT MODE (design docs)
|
||||||
|
Target: /<NAME>_vN.md (no timestamp)
|
||||||
|
- <NAME> is normalized (e.g., CONVENTIONS -> conventions)
|
||||||
|
- --version N is required (integer >= 1)
|
||||||
|
- Extension forced to .md
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
--dry-run Print the rename that would occur; do not move files.
|
||||||
|
--version N Root mode only: version number N (e.g., 1, 2, 3...)
|
||||||
|
-h, --help Show this help.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
# Category: rename to legal/20250829T171500Z_conventions_v1.md
|
||||||
|
publish-rename category legal CONVENTIONS_v1.md conventions_v1
|
||||||
|
|
||||||
|
# Category: rename a text file into naics with a slug
|
||||||
|
publish-rename category naics notes.txt procurement_policy_notes
|
||||||
|
|
||||||
|
# Root: versioned design doc at repo root (CONVENTIONS_v1.md)
|
||||||
|
publish-rename root --version 1 CONVENTIONS.md CONVENTIONS
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
- This tool changes only the filename/location; it does not git add/commit.
|
||||||
|
- For legal citations, always use the CID after publishing; the repo path
|
||||||
|
is for structure and operator convenience only.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
vMAN 1.0 — tailored for ipfs.kane-il.us
|
||||||
|
|
||||||
|
MAN
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------- argparse ----------
|
||||||
|
[[ $# -lt 1 ]] && { print_man; exit 1; }
|
||||||
|
|
||||||
|
mode="$1"; shift || true
|
||||||
|
|
||||||
|
dry_run=0
|
||||||
|
root_version=''
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--dry-run) dry_run=1; shift;;
|
||||||
|
-h|--help) print_man; exit 0;;
|
||||||
|
--version) shift; root_version="${1:-}"; [[ -z "$root_version" ]] && err "--version requires a number"; shift;;
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
case "$mode" in
|
||||||
|
category)
|
||||||
|
[[ $# -ne 3 ]] && { print_man; err "category mode requires <category> <file> <slug>"; }
|
||||||
|
category_raw="$1"; file="$2"; slug_raw="$3"
|
||||||
|
|
||||||
|
category="$(slugify "$category_raw")"
|
||||||
|
case "$category" in
|
||||||
|
legal|civics|naics|barter|w3pbs|onet) : ;;
|
||||||
|
*) err "invalid category '$category_raw' (expected: legal|civics|naics|barter|w3pbs|onet)";;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[[ -f "$file" ]] || err "file not found: $file"
|
||||||
|
ext="${file##*.}"; [[ "$ext" =~ $ALLOWED_EXT_REGEX ]] || err "extension '.$ext' not allowed (md|txt|jsonl)"
|
||||||
|
|
||||||
|
slug="$(slugify "$slug_raw")"; [[ -n "$slug" ]] || err "slug normalization produced empty value"
|
||||||
|
stamp="$(ts)"
|
||||||
|
dest_dir="$category"
|
||||||
|
dest_file="${stamp}_${slug}.${ext}"
|
||||||
|
dest_path="${dest_dir}/${dest_file}"
|
||||||
|
|
||||||
|
[[ $dry_run -eq 1 ]] && { printf 'DRY-RUN: %s -> %s\n' "$file" "$dest_path"; exit 0; }
|
||||||
|
|
||||||
|
mkdir -p "$dest_dir"
|
||||||
|
mv -- "$file" "$dest_path"
|
||||||
|
printf 'Renamed %s -> %s\n' "$file" "$dest_path"
|
||||||
|
;;
|
||||||
|
|
||||||
|
root)
|
||||||
|
# root design docs: <NAME>_vN.md at repo root
|
||||||
|
[[ -z "$root_version" ]] && { print_man; err "root mode requires --version <N>"; }
|
||||||
|
[[ $# -ne 2 ]] && { print_man; err "root mode requires <file> <name>"; }
|
||||||
|
|
||||||
|
file="$1"; name_raw="$2"
|
||||||
|
[[ -f "$file" ]] || err "file not found: $file"
|
||||||
|
|
||||||
|
# version must be integer >=1
|
||||||
|
[[ "$root_version" =~ ^[0-9]+$ ]] || err "version must be an integer"
|
||||||
|
[[ "$root_version" -ge 1 ]] || err "version must be >= 1"
|
||||||
|
|
||||||
|
name="$(slugify "$name_raw")"; [[ -n "$name" ]] || err "name normalization produced empty value"
|
||||||
|
dest_path="${name}_v${root_version}.md"
|
||||||
|
|
||||||
|
[[ $dry_run -eq 1 ]] && { printf 'DRY-RUN: %s -> %s\n' "$file" "$dest_path"; exit 0; }
|
||||||
|
|
||||||
|
mv -- "$file" "$dest_path"
|
||||||
|
printf 'Renamed %s -> %s\n' "$file" "$dest_path"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
print_man; err "unknown mode '$mode' (use 'category' or 'root')"
|
||||||
|
;;
|
||||||
|
esac
|
Loading…
Reference in New Issue