Compare commits
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Skill(pql)",
|
||||
"Skill(clide)",
|
||||
"Skill(whats-next)",
|
||||
"Skill(git-commit)",
|
||||
"Bash(git add *)",
|
||||
"Bash(git commit *)",
|
||||
"Bash(git status *)",
|
||||
@@ -30,6 +34,8 @@
|
||||
"Bash(make *)",
|
||||
"Bash(pql)",
|
||||
"Bash(pql *)",
|
||||
"Bash(clide)",
|
||||
"Bash(clide *)",
|
||||
"Bash(awk *)"
|
||||
],
|
||||
"deny": [
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": "1.5.0",
|
||||
"hash": "sha256:8090b99552c828ce019aa3740d8a0acb4768a443704b4e935efa23b32e91128d",
|
||||
"installed_at": "2026-06-02T08:46:25Z"
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
---
|
||||
name: clean-house
|
||||
description: >
|
||||
Run a documentation-discipline pass over a DQR (Decisions / Questions /
|
||||
Rejected) markdown system. Audits decisions/, surfaces drift (broken
|
||||
anchor links, missing Q→D backlinks, sunset-shaped phrases without
|
||||
linked tickets, files over the split threshold, dead cross-references),
|
||||
batches findings by category, and uses AskUserQuestion to apply fixes
|
||||
interactively. Trigger this skill whenever the user asks to clean,
|
||||
tidy, audit, sweep, garden, or review the state of decisions/, the
|
||||
DQR system, the questions index, or the records under decisions/.
|
||||
Also trigger before a release, after a session that touched many
|
||||
records, or whenever the user mentions documentation drift, stale
|
||||
plans, or "house" / "clean-house" / "house cleaning". Imperative —
|
||||
apply fixes, not just report.
|
||||
---
|
||||
|
||||
# clean-house
|
||||
|
||||
A periodic gardening pass over `decisions/`. The DQR system is
|
||||
markdown-as-source-of-truth; `.pql/pql.db` is a derived index. This skill
|
||||
operates on the markdown files, batches drift by category, and uses
|
||||
`AskUserQuestion` to decide what to apply.
|
||||
|
||||
The skill is **imperative**. It really cleans. The verb-noun match is
|
||||
load-bearing — a `clean-house` that only produced a report would be
|
||||
misnamed. Mechanical fixes get a single batched approval, judgment-call
|
||||
findings get individual prompts, and a "stop asking" escape hatch is
|
||||
always present.
|
||||
|
||||
You — Claude — are the runtime. Read this file, read
|
||||
`references/rules.md`, follow the procedure below, call `pql` and `Edit`
|
||||
to do the work, call `AskUserQuestion` for the prompts.
|
||||
|
||||
## When to run
|
||||
|
||||
- Before a release.
|
||||
- After a session that touched many D / Q / R records.
|
||||
- When something feels stale and a sweep is wanted.
|
||||
- When `pql decisions validate` is green but `decisions/` still feels off.
|
||||
|
||||
This skill is **not** a replacement for `pql decisions validate`.
|
||||
Validate is fast, hot, and gates pre-push. clean-house is slow, cold,
|
||||
on-demand. Escalation path: rules that fire often here are candidates
|
||||
for promotion to `pql decisions validate`; rules that almost never fire
|
||||
stay here where false positives don't slow anyone down.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. **Gate on validate.** Run `pql decisions validate`. If it exits
|
||||
non-zero, stop and tell the user to fix validator findings first.
|
||||
Do not proceed.
|
||||
|
||||
2. **Load the rule catalog.** Read `references/rules.md`. Each rule
|
||||
names its detection, fix, and finding-id format.
|
||||
|
||||
3. **Probe conventions.** Read `decisions/.clean-house.yaml` if it
|
||||
exists; otherwise infer from the project. Conventions to resolve:
|
||||
- `heading_level`: 2 or 3 — the depth records use (`## D-N` vs
|
||||
`### D-N`). Probe by sampling the first 3 records' file
|
||||
locations and looking for the first matching ATX heading.
|
||||
- `backlink_phrasing`: the project's Q→D backlink phrase (default
|
||||
`Resolved → D-N`; some projects use `Resolved as D-N` or
|
||||
`→ D-N`).
|
||||
- `file_threshold`: default 350 (RULE-FILE-OVER-THRESHOLD).
|
||||
- `stale_open_q_days`: default 60 (RULE-STALE-OPEN-Q).
|
||||
|
||||
Pass the resolved conventions into each rule's detection. Cache
|
||||
the probe result for the run.
|
||||
|
||||
`decisions/.clean-house.yaml` shape (all keys optional):
|
||||
```yaml
|
||||
heading_level: 3
|
||||
backlink_phrasing: "Resolved → "
|
||||
file_threshold: 500
|
||||
stale_open_q_days: 90
|
||||
exclude_paths: ["legacy/", "drafts/"]
|
||||
```
|
||||
|
||||
4. **Sync the index.** Run `pql decisions sync` so the DB reflects
|
||||
the markdown.
|
||||
|
||||
5. **Walk decisions/.** Use `pql decisions list` to enumerate
|
||||
records, plus `pql decisions read <id>` per record when a rule
|
||||
needs the body. For file-level rules (size, sort), read the
|
||||
markdown files directly. **Filter out** any record whose
|
||||
`file_path` begins with `legacy/` (or any prefix in
|
||||
`exclude_paths` from the probe).
|
||||
|
||||
6. **Run each rule's detection.** Collect findings. Tag each with
|
||||
its rule ID, finding ID (per the rule's format), category
|
||||
(`mechanical` | `judgment`), and the record(s)/file(s) involved.
|
||||
|
||||
7. **Group findings by rule** and prompt. Honor any "skipped 3+
|
||||
times" findings from the skip ledger by escalating their prompt
|
||||
("skipped 3x — promote to manual decision, downgrade to
|
||||
summary-only, or keep asking?"):
|
||||
|
||||
- **Mechanical rules** — one prompt per rule, batched. Show the
|
||||
count and offer: apply all / show diff first / skip this rule /
|
||||
**stop asking** (global — see below).
|
||||
- **Judgment rules** — one prompt per finding. Options match the
|
||||
rule's `Fix` section, plus the same global **stop asking**.
|
||||
|
||||
**"Stop asking" semantics (global).** When the user picks "stop
|
||||
asking" on any prompt, immediately halt all further prompts in
|
||||
the run — mechanical batches not yet asked AND remaining
|
||||
judgment findings. Move all unprompted findings to the skip
|
||||
ledger marked `deferred-stop-asking`. Skip to step 9.
|
||||
|
||||
**"Show diff first" loop.** When the user picks "show diff
|
||||
first" on a mechanical batch, emit a unified diff of the staged
|
||||
edits (one block per file), then re-prompt the same question
|
||||
with the same options minus "show diff first" — preventing
|
||||
infinite loops while still letting the user inspect before
|
||||
committing.
|
||||
|
||||
8. **Apply approvals.** For mechanical rules use `Edit` to mutate
|
||||
the markdown directly. For judgment rules, follow the option the
|
||||
user picked (file a ticket via `pql ticket new`, mark superseded
|
||||
by editing the record, etc.).
|
||||
|
||||
9. **Update the skip ledger and history.** Both files live at
|
||||
`decisions/.clean-house-state.md`, gitignored. The file uses
|
||||
per-run section headers so it doubles as run history:
|
||||
|
||||
```markdown
|
||||
# clean-house run history
|
||||
|
||||
## 2026-05-06T13:42Z
|
||||
fired: RULE-ANCHOR-DRIFT(1) RULE-SUNSET-WITHOUT-TICKET(1)
|
||||
applied: RULE-ANCHOR-DRIFT(1) RULE-SUNSET-WITHOUT-TICKET(1)
|
||||
skipped:
|
||||
- RULE-ANCHOR-DRIFT D-8:#q-1-markdown-mirror-for-tickets
|
||||
|
||||
## 2026-05-04T11:10Z
|
||||
fired: ...
|
||||
```
|
||||
|
||||
Each section starts with a UTC ISO-8601 timestamp. `fired`
|
||||
counts findings detected; `applied` counts findings the user
|
||||
acted on; `skipped` lists `<rule-id> <finding-id>` pairs.
|
||||
|
||||
**Promotion-candidate computation.** Scan the most recent N=7
|
||||
sections (or all sections if fewer). A rule is a promotion
|
||||
candidate if it `fired` in ≥ 5 of those sections. Surface the
|
||||
candidates in the summary; do not auto-promote.
|
||||
|
||||
10. **Emit the summary** (see Output below).
|
||||
|
||||
## Question phrasing
|
||||
|
||||
Always include the rule ID in the prompt so the user learns what's
|
||||
being checked.
|
||||
|
||||
**Mechanical (batched):**
|
||||
> RULE-ANCHOR-DRIFT: 12 cross-reference anchors point to headings that
|
||||
> no longer exist (renamed or deleted). Apply all 12 fixes? Options:
|
||||
> apply all / show diff first / skip this rule / stop asking, summarize
|
||||
> the rest.
|
||||
|
||||
**Judgment (individual):**
|
||||
> RULE-SUNSET-WITHOUT-TICKET: D-59 mentions "must track dugite-native
|
||||
> releases for security updates" but has no linked T. Options: file T
|
||||
> now / draft a T description first / mark as already covered (add note
|
||||
> to D) / skip / stop asking.
|
||||
|
||||
Phrasing rules:
|
||||
|
||||
- **Always include "stop asking, summarize the rest."** Sessions
|
||||
without bandwidth for full review need an escape. Forcing answers
|
||||
to every question kills the tool.
|
||||
- **Always include "skip."** Skipped findings go to the ledger; three
|
||||
consecutive skips of the same finding promote the next prompt.
|
||||
- **Mechanical batches; judgment doesn't.** "Apply all 12 fixes?" is
|
||||
one decision against a clear delta. Twelve individual prompts defeat
|
||||
the point. Conversely, judgment findings are each separate work —
|
||||
presenting them as a batch hides cost.
|
||||
|
||||
## What this skill does NOT do
|
||||
|
||||
- Does not file tickets without asking.
|
||||
- Does not rewrite D-record body prose, only metadata fields and
|
||||
cross-reference links.
|
||||
- Does not touch records under `legacy/`.
|
||||
- Does not run `pql decisions validate` *as a fix* — only as a
|
||||
precondition gate.
|
||||
- Does not promote rules to `pql decisions validate` automatically.
|
||||
Promotion is a human decision; this skill only flags candidates
|
||||
("RULE-X has fired in 5 of the last 7 runs — consider promoting").
|
||||
|
||||
## Non-interactive context
|
||||
|
||||
If invoked without an interactive `AskUserQuestion` surface (a CI run,
|
||||
a batch script), refuse to apply judgment fixes. Apply mechanical
|
||||
fixes only if the user explicitly says so via the trigger phrase
|
||||
("auto-apply mechanical, skip judgment"); otherwise emit the summary
|
||||
and exit without mutating files.
|
||||
|
||||
## Output
|
||||
|
||||
At the end, emit a summary block:
|
||||
|
||||
```
|
||||
clean-house — sweep complete
|
||||
────────────────────────────
|
||||
Files scanned: 11
|
||||
Records parsed: 62 D, 23 Q, 11 R
|
||||
Conventions: heading_level=3 backlink="Resolved → "
|
||||
Mechanical fixes: 14 applied, 0 deferred
|
||||
Judgment findings: 3 acted on, 2 deferred (in state file)
|
||||
Promotion candidates: RULE-ANCHOR-DRIFT (5/7 runs)
|
||||
Touched files: decisions/architecture.md, decisions/questions.md
|
||||
```
|
||||
|
||||
If this is the first run (no prior history) or there are fewer
|
||||
than 5 prior runs, `Promotion candidates:` reports
|
||||
`(none — N/7 runs of history)` instead of decorative empty content.
|
||||
|
||||
The summary names the touched files so the next commit message can be
|
||||
honest about what changed. The skill does not commit on its own.
|
||||
|
||||
## Versioning
|
||||
|
||||
clean-house ships embedded in the pql binary; its version is the
|
||||
pql version (`pql --version`). To see what changed and when, read
|
||||
`CHANGELOG.md` or `git log internal/skill/clean-house/` in the pql
|
||||
repo. Keep this file and `references/rules.md` consistent with
|
||||
each other when iterating — they're the contract.
|
||||
@@ -0,0 +1,386 @@
|
||||
# clean-house rule catalog
|
||||
|
||||
The rules clean-house runs and the reasoning behind each. New rules
|
||||
land here as their own entry. Catalog churn — additions, refinements,
|
||||
retirements — is tracked in pql's `CHANGELOG.md` and `git log
|
||||
internal/skill/clean-house/`. When a rule is retired, leave a
|
||||
**Retired** stub (ID + one-line reason + retirement commit) so the
|
||||
trail of "we used to check X" is recoverable from this file alone.
|
||||
|
||||
## Reading a rule
|
||||
|
||||
Each entry below has:
|
||||
|
||||
- **ID** — Stable identifier (e.g. `RULE-ANCHOR-DRIFT`). Used in
|
||||
AskUserQuestion prompts and skip-ledger entries so the user learns
|
||||
what's being checked.
|
||||
- **Category** — `mechanical` or `judgment`. Drives whether findings
|
||||
batch into one prompt or each get their own.
|
||||
- **Finding ID** — Format string for the per-finding stable
|
||||
identifier. Skip-ledger entries are written as `<rule-id>
|
||||
<finding-id> <ISO-date>`; subsequent runs use the same format to
|
||||
recognize "same finding skipped 3 runs in a row" and escalate.
|
||||
Use only stable inputs (record IDs, file paths, slug strings, body
|
||||
hashes) — never line numbers or timestamps.
|
||||
- **Detection** — Concrete steps to find violations. Names the
|
||||
pql command or file primitive used.
|
||||
- **Fix** — Mechanical: deterministic action. Judgment: the prompt
|
||||
option list and what each option does.
|
||||
- **Why** — One paragraph on the failure mode this rule guards
|
||||
against. Future-you reading the rule benefits from the reasoning,
|
||||
not just the check.
|
||||
|
||||
---
|
||||
|
||||
## RULE-ANCHOR-DRIFT
|
||||
|
||||
**Category:** mechanical
|
||||
|
||||
**Finding ID:** `<source-record>:<link-target>` (e.g.
|
||||
`D-8:#q-1-markdown-mirror-for-tickets` or
|
||||
`D-8:questions.md#q-1`).
|
||||
|
||||
**Detection:**
|
||||
|
||||
Anchor-only markdown links (`[text](#slug)`) resolve against the
|
||||
**source file's full heading set**, not the body of a single record
|
||||
— record-level headings (`### D-N: …`) live as siblings in the
|
||||
file and are valid link targets. The previous detection (which used
|
||||
`pql decisions read`'s body-only `headings` array) missed these
|
||||
and produced false positives.
|
||||
|
||||
Per source body:
|
||||
|
||||
1. Open the source body's file (`<vault>/<file_path>` from
|
||||
`pql decisions list`) and extract every ATX heading. Build a
|
||||
slug index for the file using the GFM convention (lowercase,
|
||||
hyphenate spaces, drop punctuation, disambiguate duplicates with
|
||||
`-1`/`-2`). Cache per file — every record in that file uses the
|
||||
same index.
|
||||
2. For each `[text](target)` link in the body:
|
||||
- Anchor-only (`#slug`): check `slug` against the **source file's**
|
||||
index. Missing → flag.
|
||||
- Cross-file (`path.md#slug`): resolve `path.md` relative to the
|
||||
source file's directory; check `slug` against that file's
|
||||
index. Missing file or missing slug → flag.
|
||||
|
||||
**Fix:**
|
||||
|
||||
If the slug exists in a sibling file's index (same directory) and
|
||||
the link is anchor-only, rewrite to `path.md#slug` (the canonical
|
||||
cross-file form). If the slug exists in the source file's index
|
||||
with edit-distance ≤ 2 from the link target, rewrite to the
|
||||
matched slug. Otherwise downgrade to judgment ("no auto-fix; the
|
||||
heading was removed, not renamed") with options: drop the link /
|
||||
point elsewhere / mark as intentionally dangling.
|
||||
|
||||
**Fix:**
|
||||
|
||||
If exactly one heading with a closely-matching slug exists (slug
|
||||
edit-distance ≤ 2 or substring match), rewrite the link to point at
|
||||
that heading. If no plausible match exists, downgrade the finding to
|
||||
judgment ("no auto-fix; the heading was removed, not renamed") and
|
||||
prompt the user with options: drop the link / point elsewhere / mark
|
||||
as intentionally dangling.
|
||||
|
||||
**Why:**
|
||||
|
||||
When a heading is renamed for clarity, every cross-reference pointing
|
||||
at the old slug silently breaks. The rendered docs still look fine —
|
||||
the link just goes nowhere. Without periodic sweeps these decay
|
||||
indefinitely; the cost of the sweep is small compared to the cost of
|
||||
a reader following a dead link and losing trust in the index.
|
||||
|
||||
---
|
||||
|
||||
## RULE-MISSING-Q-BACKLINK
|
||||
|
||||
**Category:** mechanical
|
||||
|
||||
**Finding ID:** `<d-id>:<q-id>` (e.g. `D-7:Q-2`). Order is always
|
||||
D-first regardless of which side is missing the backlink.
|
||||
|
||||
**Detection:**
|
||||
|
||||
For each D record body, find lines of the shape `Resolves: Q-N`
|
||||
(plain text, typically in the metadata block at the top of the
|
||||
record). For each Q-N referenced, fetch that Q record's body via
|
||||
`pql decisions read Q-N` and check whether it contains a line of the
|
||||
shape `Resolved → D-N` (or equivalent backlink phrasing — match the
|
||||
project's own convention; default pattern is `Resolved → D-N`).
|
||||
|
||||
Also check the reverse direction: any Q record claiming
|
||||
`Resolved → D-N` whose D-N body lacks `Resolves: Q-N`.
|
||||
|
||||
**Fix:**
|
||||
|
||||
Insert the missing backlink in-place using `Edit`:
|
||||
|
||||
- Missing on the D side: add `Resolves: Q-N` to the D's metadata
|
||||
block (typically right after `Domain:` / `Status:` lines).
|
||||
- Missing on the Q side: append `Resolved → D-N` to the Q's status
|
||||
line (or in the conventional position for that project).
|
||||
|
||||
**Why:**
|
||||
|
||||
Bidirectional Q↔D links are the navigation backbone of the DQR
|
||||
system. When one side drifts, search-by-decision finds nothing for
|
||||
that question and search-by-question doesn't surface its resolution.
|
||||
The asymmetric state usually arises from a hand-edit on one record
|
||||
that forgot to update the other; the fix is purely mechanical because
|
||||
the correct content is already determined by the existing pointer
|
||||
in the other direction.
|
||||
|
||||
---
|
||||
|
||||
## RULE-RECORD-SORT
|
||||
|
||||
**Category:** mechanical
|
||||
|
||||
**Finding ID:** `<file-path>` (e.g. `decisions/architecture.md`).
|
||||
The whole file is one finding — sort applies to the file as a
|
||||
unit.
|
||||
|
||||
**Detection:**
|
||||
|
||||
For each `decisions/*.md` file, read it directly (no pql) and find
|
||||
all top-level `## D-N` / `## Q-N` / `## R-N` headings in order. Strip
|
||||
the prefix, parse the numeric suffix, and check whether the sequence
|
||||
is monotonically ascending within each ID family (D, Q, R kept
|
||||
separate — files commonly mix families).
|
||||
|
||||
Only flag a file if it is **otherwise tidy** — currently no out-of-
|
||||
order amendments interleaved with new records. The heuristic: if the
|
||||
sort would touch fewer than three records out of position, apply it;
|
||||
if more, downgrade to judgment ("this file looks deliberately
|
||||
arranged — confirm before reordering"). Three is a soft threshold; if
|
||||
the file's last commit message contains `WIP` or `do-not-sort`, skip.
|
||||
|
||||
**Fix:**
|
||||
|
||||
Reorder the records within the file so each ID family is ascending.
|
||||
Preserve everything else (headings between record blocks, any prose
|
||||
prelude/postlude). Use `Edit` with full block replacement, not in-
|
||||
place line shuffling — easier to verify the diff.
|
||||
|
||||
**Why:**
|
||||
|
||||
Records added at the bottom of a file are easy to write but hard to
|
||||
find. Sorted IDs let a reader find D-37 by jumping to the
|
||||
two-thirds mark of the file rather than scanning. The cost of the
|
||||
sort is one reordering pass; the benefit is paid back on every
|
||||
subsequent read.
|
||||
|
||||
---
|
||||
|
||||
## RULE-EOF-NORMALIZATION
|
||||
|
||||
**Category:** mechanical
|
||||
|
||||
**Finding ID:** `<file-path>` (one finding per file).
|
||||
|
||||
**Detection:**
|
||||
|
||||
For each `decisions/*.md` file (and any other markdown the skill
|
||||
touched during this run), read directly. Flag if:
|
||||
|
||||
- File does not end with exactly one `\n`.
|
||||
- Any line contains trailing whitespace before its `\n`.
|
||||
|
||||
**Fix:**
|
||||
|
||||
Trim trailing whitespace from each line. Ensure exactly one trailing
|
||||
newline at end of file. Use `Edit` only if a violation was found —
|
||||
this rule must not produce a no-op diff.
|
||||
|
||||
**Why:**
|
||||
|
||||
Editor and git config drift causes whitespace creep that's invisible
|
||||
in rendering but pollutes diffs (every record edit ends up touching
|
||||
unrelated lines). A periodic normalization keeps future diffs clean
|
||||
without forcing per-editor enforcement on every contributor.
|
||||
|
||||
---
|
||||
|
||||
## RULE-SUNSET-WITHOUT-TICKET
|
||||
|
||||
**Category:** judgment
|
||||
|
||||
**Finding ID:** `<record-id>:<phrase-hash-8>` where phrase-hash-8 is
|
||||
the first 8 hex chars of `sha256(<matched-phrase>)`. Lets the
|
||||
ledger distinguish two sunset phrases in the same record.
|
||||
|
||||
**Detection:**
|
||||
|
||||
For each D record body (via `pql decisions read <id>`), grep for
|
||||
sunset-shaped phrases. Default regex set:
|
||||
|
||||
```
|
||||
(?i)\b(delete|remove|sunset|kill[ -]?switch|tear[ -]?down) when\b
|
||||
(?i)\bmust (track|monitor|watch|follow)\b
|
||||
(?i)\brevisit (when|after|once)\b
|
||||
(?i)\b(deprecate|retire) (when|after|once)\b
|
||||
```
|
||||
|
||||
For each match, run `pql ticket list --decision <id>`. If no tickets
|
||||
exist, the D has work-shaped intent without a tracked T — flag.
|
||||
|
||||
**Fix (prompt options):**
|
||||
|
||||
- **File T now** — Run `pql ticket new task "<phrase>" --decision <id>`,
|
||||
then prompt the user for a description (or call `pql ticket refine
|
||||
write <T> --description ...` after creation).
|
||||
- **Draft a T description first** — Open an `AskUserQuestion` for the
|
||||
description, then file as above.
|
||||
- **Mark as already covered** — Add a note to the D body
|
||||
("Tracked under T-N") via `Edit`. Skill does not assert which T;
|
||||
user provides the ID.
|
||||
- **Skip** — Adds to the ledger.
|
||||
- **Stop asking** — Skip remaining sunset findings, summarize.
|
||||
|
||||
**Why:**
|
||||
|
||||
Sunset-shaped intent ("we'll handle X when Y happens") is the most
|
||||
common source of accumulated debt in a DQR system: the trigger
|
||||
condition arrives, nobody remembers the D, the work doesn't happen.
|
||||
Linking each sunset to a T is the simplest defense — a T is a
|
||||
backlog item that surfaces in `plan whatsnext` / `plan board`. The
|
||||
fix is judgment because the right action depends on whether the D's
|
||||
condition is still relevant, whether it's already covered, and what
|
||||
the right scope is for the resulting T.
|
||||
|
||||
---
|
||||
|
||||
## RULE-FILE-OVER-THRESHOLD
|
||||
|
||||
**Category:** judgment
|
||||
|
||||
**Finding ID:** `<file-path>` (one finding per file; threshold
|
||||
choice is judgment, not a per-line problem).
|
||||
|
||||
**Detection:**
|
||||
|
||||
For each `decisions/*.md` file, count lines (via `wc -l` or
|
||||
equivalent). Default threshold: **350 lines**. Configurable per
|
||||
project — read `decisions/.clean-house.yaml` if present, key
|
||||
`file_threshold`. Fall back to 350.
|
||||
|
||||
**Fix (prompt options):**
|
||||
|
||||
- **Split now (which axis?)** — Prompt for the split axis: by ID
|
||||
family (D/Q/R), by domain, by date range, custom. Then perform the
|
||||
split: create new file(s), move records, update any anchor links
|
||||
pointing into the moved records (run RULE-ANCHOR-DRIFT in apply
|
||||
mode against the affected files after the split).
|
||||
- **Accept and raise the threshold** — Update
|
||||
`decisions/.clean-house.yaml`'s `file_threshold` to the next
|
||||
reasonable round number above the current line count.
|
||||
- **Defer** — Skip until next run.
|
||||
|
||||
**Why:**
|
||||
|
||||
A single decisions file growing past ~350 lines is the point at which
|
||||
linear scanning starts to lose to grep, and where the cost of
|
||||
splitting (renaming anchors) is still small. Beyond ~600 lines the
|
||||
split cost compounds. The threshold is judgment because some projects
|
||||
deliberately keep one file per domain and accept the size; others
|
||||
split aggressively. The skill surfaces the question; it doesn't
|
||||
decide.
|
||||
|
||||
---
|
||||
|
||||
## RULE-DEAD-FILE-REFERENCE
|
||||
|
||||
**Category:** judgment
|
||||
|
||||
**Finding ID:** `<record-id>:<path-token>` (path token as written
|
||||
in the body, not resolved).
|
||||
|
||||
**Detection:**
|
||||
|
||||
For each D record body (via `pql decisions read <id>`), grep for
|
||||
path-shaped tokens. Default regex:
|
||||
|
||||
```
|
||||
\b([\w./-]+\.(md|go|py|sql|yaml|yml|toml))\b
|
||||
```
|
||||
|
||||
The first detection pass produced 6/6 false positives in
|
||||
real-world use; the regex is necessary but not sufficient. For
|
||||
each match, apply the filters below in order — if any matches,
|
||||
**skip without flagging**:
|
||||
|
||||
1. **Placeholder filter.** Token contains `T-NNN`, `D-NNN`,
|
||||
`Q-NNN`, `R-NNN`, `...`, `<`, `>`, or `*` — it's a pattern,
|
||||
not a real path.
|
||||
2. **Source-relative resolution.** Resolve the token against the
|
||||
source file's directory (`<vault>/<file_path>`'s dir). If
|
||||
`[ -e <resolved> ]`, the reference is live.
|
||||
3. **Repo-relative resolution.** If `[ -e <token> ]` from the
|
||||
repo root, the reference is live.
|
||||
4. **Basename-fallback.** Run `find <repo-root> -name <basename>`
|
||||
(where basename is the last path component). If exactly one
|
||||
match exists, treat the reference as live and emit a low-
|
||||
priority "consider rewriting to the absolute path" note (not a
|
||||
judgment finding). If multiple matches exist, do not flag —
|
||||
the token is too ambiguous.
|
||||
|
||||
Only after all four filters miss does the reference qualify as
|
||||
truly dead and warrant a judgment prompt.
|
||||
|
||||
**Fix (prompt options):**
|
||||
|
||||
- **Update reference** — Prompt for the new path; rewrite the
|
||||
reference via `Edit`. If the user types a path, validate it exists
|
||||
before applying.
|
||||
- **Mark superseded** — Add a note to the D ("This decision
|
||||
references files no longer present; the underlying constraint
|
||||
was retired by D-N") and prompt for the superseding D ID.
|
||||
- **Defer** — Skip until next run.
|
||||
|
||||
**Why:**
|
||||
|
||||
Decisions reference code paths to ground their reasoning in the
|
||||
codebase that motivated them. When the code moves or gets deleted,
|
||||
the D's reasoning becomes harder to verify. Dead refs are a signal
|
||||
that either the decision should be updated or the decision itself is
|
||||
no longer load-bearing — both are judgment calls the user needs to
|
||||
make.
|
||||
|
||||
---
|
||||
|
||||
## RULE-STALE-OPEN-Q
|
||||
|
||||
**Category:** judgment
|
||||
|
||||
**Finding ID:** `<q-id>` (one finding per Q-record; staleness
|
||||
threshold is global per run, so a Q is either stale or not).
|
||||
|
||||
**Detection:**
|
||||
|
||||
Run `pql decisions list --type question --status open`. For each
|
||||
returned record, check the `date` field. Default staleness threshold:
|
||||
**60 days** (configurable via `decisions/.clean-house.yaml` key
|
||||
`stale_open_q_days`). Flag any Q with `date` older than the
|
||||
threshold.
|
||||
|
||||
**Fix (prompt options):**
|
||||
|
||||
- **Still relevant** — Touch the Q's `date` field to today, optionally
|
||||
add a one-line "still open as of YYYY-MM-DD: <reason>" to the body.
|
||||
- **Nudge to load-bearing index** — If the project keeps a
|
||||
load-bearing Qs list (a curated index of unresolved questions
|
||||
affecting current work), prompt for inclusion and add the Q there.
|
||||
- **Mark withdrawn** — Change the Q's status to `withdrawn` (the DQR
|
||||
system's terminal state for "no longer worth answering"); the
|
||||
user supplies a one-line reason.
|
||||
- **Defer** — Skip until next run.
|
||||
|
||||
**Why:**
|
||||
|
||||
Open questions are useful when they're current; stale ones become
|
||||
ambient noise that drowns the signal of new questions. Most
|
||||
projects don't enforce explicit Q-lifecycle, so without a periodic
|
||||
prompt the open list grows forever. The right action depends on
|
||||
whether the question is still open in fact — not just in metadata —
|
||||
which only the user can confirm.
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
name: clide
|
||||
description: >
|
||||
Use when you are running inside the clide IDE and want to observe or drive
|
||||
its live UI — panes, editor, files, git, readers, toasts, layout — through
|
||||
the `clide` CLI, or to find out what commands clide exposes. clide is the
|
||||
IDE hosting this session; it puts `clide` on your PATH and a per-workspace
|
||||
socket in `CLIDE_SOCK`. Start with `clide capabilities` to enumerate the
|
||||
live tool surface. Triggers: "what can clide do", "drive the clide UI",
|
||||
"open this in clide", "show the user", "toast", or invoking /clide.
|
||||
user-invocable: true
|
||||
allowed-tools: Bash
|
||||
---
|
||||
|
||||
# Driving clide from the CLI
|
||||
|
||||
You are (often) running **inside clide** — a Flutter IDE that hosts this
|
||||
Claude session. It exposes its whole UI surface as a `clide <subsystem> <verb>`
|
||||
CLI on your PATH, talking to the running app over a per-workspace socket
|
||||
(`CLIDE_SOCK`). Every UI action the user can take has a CLI verb, and every
|
||||
verb's effect is observable — that is the parity contract (D-6). So you can
|
||||
*see what the user sees* and *show the user what you mean*.
|
||||
|
||||
## Discover the surface first — don't hard-code it
|
||||
|
||||
The authoritative, always-current list of commands is the app itself:
|
||||
|
||||
```
|
||||
clide capabilities
|
||||
```
|
||||
|
||||
It returns JSON: every registered command, split into `subsystem` + `verb`,
|
||||
with its argument schema (`positional` order + per-arg `type`/`required`/
|
||||
constraints) where one is declared. **Sourced from the live dispatcher
|
||||
registry, so it never drifts.** New panels/verbs appear here the moment they
|
||||
register — re-run it instead of trusting a remembered list (including this
|
||||
one). `clide <subsystem>` with no verb, or an unknown command, prints usage.
|
||||
|
||||
## The two halves of parity
|
||||
|
||||
- **Observe** — read the live UI state:
|
||||
- `clide status` — one-shot orientation: workspace, git, active editor
|
||||
buffer + selection, open reader docs, the panes the user sees, layout.
|
||||
- `clide pane list`, `clide editor active` — narrower snapshots.
|
||||
- **Drive** — make the UI do something:
|
||||
- `clide ui open <reader> <ref>` — open a doc in a GUI reader
|
||||
(`tickets`/`decisions` by id, `markdown`/`diff` by path): "look at this
|
||||
with me." `diff <path>` reveals the diff tab and scrolls to that file.
|
||||
- `clide ui toast "message" [--severity success|warning|error|info]` — raise
|
||||
a toast on the user's screen: "tests green", "push failed".
|
||||
- pane/editor/files/git verbs — see `clide capabilities` for the current set
|
||||
and their args.
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Slots:** the layout has three content slots — `sidebar` (left), `workspace`
|
||||
(center, where Claude lives), `context` (right) — plus the bottom `statusbar`.
|
||||
Many verbs take a slot.
|
||||
- **Honest failures:** a drive verb with no live GUI returns a `toolError`
|
||||
("no live UI to drive"), not a hang. JSON on stdout; exit code conveys
|
||||
ok/usage/tool error.
|
||||
- **You only see what flows through clide.** Your own non-`clide` shell work
|
||||
(plain file reads, `make test`, `git`) is outside clide's view by design
|
||||
(D-83) — run it *through* `clide …` if you want clide to observe it.
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: commit
|
||||
description: Create a well-formatted git commit with staged changes
|
||||
---
|
||||
|
||||
# Git Commit
|
||||
|
||||
Create a well-formatted commit with staged changes following best practices.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Run `git status --porcelain` to check for changes
|
||||
2. If no staged changes, show unstaged files and ask what to stage
|
||||
3. Run `git diff --cached` to review staged changes
|
||||
4. Generate a commit message following Conventional Commits format:
|
||||
- `feat:` new feature
|
||||
- `fix:` bug fix
|
||||
- `docs:` documentation
|
||||
- `refactor:` code restructuring
|
||||
- `test:` adding tests
|
||||
- `chore:` maintenance
|
||||
5. Create commit with the message, adding Co-Authored-By trailer
|
||||
|
||||
## Commit Message Format
|
||||
|
||||
```
|
||||
<type>(<scope>): <short description>
|
||||
|
||||
<body - what and why, not how>
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Warn about large commits (>500 lines changed)
|
||||
- Suggest splitting large changes into smaller commits
|
||||
- Never skip pre-commit hooks unless explicitly requested
|
||||
|
||||
## Changelog
|
||||
|
||||
After committing, update `CHANGELOG.md` following [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format:
|
||||
|
||||
1. Add entry under `[Unreleased]` section (create if not exists)
|
||||
2. Categorize changes:
|
||||
- `Added` - new features
|
||||
- `Changed` - changes to existing functionality
|
||||
- `Deprecated` - features marked for removal
|
||||
- `Removed` - removed features
|
||||
- `Fixed` - bug fixes
|
||||
- `Security` - security-related changes
|
||||
3. Write entries in imperative mood: "Add feature" not "Added feature"
|
||||
4. Reference issue numbers where applicable
|
||||
@@ -32,11 +32,36 @@ Always check first:
|
||||
|
||||
## Core Workflow
|
||||
|
||||
Wireframing is a **design conversation, not a batch job.** Produce **one screen
|
||||
at a time**, show it, and **stop for the user's reaction** before authoring the
|
||||
next. Each shown screen is a checkpoint — never fan out a whole set of
|
||||
wireframes unprompted, even if the user described several screens up front.
|
||||
|
||||
1. **Health check** — verify Frame0 is running
|
||||
2. **Write wireframe JSON** — to `docs/design/wireframes/{category}/{name}.json`
|
||||
2. **Write wireframe JSON** — to `docs/design/wireframes/{category}/{name}.json`.
|
||||
Author **one** screen.
|
||||
3. **Push to Frame0** — `frame0-sync.py push <file.json>`
|
||||
4. **Export PNG** — `frame0-sync.py export <file.json> <output.png>`
|
||||
5. **Clean up** — delete test/scratch pages from Frame0 when done
|
||||
5. **Show it inline** — surface the exported PNG in the conversation so the
|
||||
user sees the result without opening Frame0. Always do this after an export,
|
||||
and again whenever you re-export or the user asks to see it.
|
||||
- **Inside clide** (the IDE hosting this session — `CLIDE_SOCK` is set):
|
||||
render it as a native image card in the live Claude pane with
|
||||
`clide image show <path> [--caption "…"]` (T-249). This is the *proper*
|
||||
way to put an image in the clide conversation — a bare `Read` only shows
|
||||
it in the transcript, not in the UI the user is looking at.
|
||||
- **Otherwise**: `Read` the PNG so it renders inline in the transcript.
|
||||
6. **STOP and wait for approval** — after showing a screen, pause and ask the
|
||||
user for feedback. Do **not** author, push, or export the next screen until
|
||||
they give an explicit go-ahead. Iterate on the current screen until they're
|
||||
happy, then proceed to the next one — looping back to step 2 for each.
|
||||
7. **Clean up** — delete test/scratch pages from Frame0 when done
|
||||
|
||||
> **Multi-screen requests are still one-at-a-time.** If the user asks for
|
||||
> several screens, treat it as a queue: build the first, show it, get approval,
|
||||
> then move on. The only exception is an explicit, unambiguous instruction to
|
||||
> generate a batch without stopping (and even then prefer the batch-export
|
||||
> dry-run + approval flow below).
|
||||
|
||||
### Scripts
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ Entries should be short imperative phrases that describe user-facing impact —
|
||||
|
||||
### Be concise — this is the rule, not a suggestion
|
||||
|
||||
CHANGELOG entries must be **one or two short sentences**. Hard cap: **60 words per bullet** (enforced by `ci/changelog_gate.sh`). Aim for 30 or under; if you can't say it in one line wrapped at ~75 columns, you're writing the wrong document.
|
||||
CHANGELOG entries must be **one or two short sentences**. Hard cap: **60 words per bullet**, enforced by the pre-push gate — verify before committing with `make changelog-gate` (run the `make` target, not the script it wraps). Aim for 30 or under; if you can't say it in one line wrapped at ~75 columns, you're writing the wrong document.
|
||||
|
||||
The CHANGELOG is read by humans scanning for what changed between two versions. It is **not** the place for the rationale, the probe results, the implementation detail, the behavior-change deep dive, or the "see also" cross-references. Those belong in:
|
||||
|
||||
@@ -93,9 +93,10 @@ Cutting a release is its own commit. In a single commit:
|
||||
1. Move all entries from `## [Unreleased]` under a new heading `## [X.Y.Z] — YYYY-MM-DD`.
|
||||
2. Leave an empty `## [Unreleased]` section at the top with its subsection skeleton ready.
|
||||
3. Bump `pubspec.yaml` `version:` to `X.Y.Z` (drop the `-dev` suffix for the tag; re-add it on the next development commit if desired).
|
||||
4. Commit subject: `release vX.Y.Z`.
|
||||
4. Run `make gen-build-info` so `assets/licenses.yaml` `self.version:` re-syncs from pubspec (it's auto-rewritten by every build but commit the fresh state). Stage the resulting diff alongside step 3.
|
||||
5. Commit subject: `release vX.Y.Z`.
|
||||
|
||||
`pubspec.yaml` is the single source of truth for the version — the Makefile reads it for ldflag stamping of the sidecar binary, and the Flutter app reads it for build info. Bumping `pubspec.yaml` and the changelog out of sync is the mistake this rule prevents.
|
||||
`pubspec.yaml` is the single source of truth for the version. Every `make` build/run/test target regenerates `lib/src/build_info.g.dart` (gitignored) and rewrites `assets/licenses.yaml` `self.version:` from it — so the Flutter app sees the current version everywhere without manual sync. Bumping `pubspec.yaml` and the changelog out of sync is the mistake this rule prevents.
|
||||
|
||||
## Attribution trailer
|
||||
|
||||
@@ -133,6 +134,10 @@ Never pass multi-line messages via `-m "line1\nline2"` or multiple `-m` flags
|
||||
- SQLite index files (`*.sqlite`, `*.sqlite-wal`, `*.sqlite-shm`, `*.db`) — caches generated against local repos; must never land here. Gitignored defensively.
|
||||
- Coverage / test output (`*.out`, `coverage.*`, `*.test`) — gitignored.
|
||||
|
||||
## Don't hand-manage `.pql/changelog`
|
||||
|
||||
The pre-commit hook exports the pql ticket DB and **auto-stages `.pql/changelog/` on every commit**. Don't `git add .pql/changelog` yourself and don't write a dedicated "flush the export" commit — just make your normal commit and the hook sweeps the ticket state in. The only thing to remember: a turn that files/changes a ticket but makes **zero commits** never fires the hook, so the change won't persist (and a later branch switch can drop it). The fix is simply to make a commit — you don't need to touch `.pql/changelog`.
|
||||
|
||||
## Safety reminders (reinforced from the global Claude Code protocol)
|
||||
|
||||
- **Never** `--no-verify`. If a pre-commit hook fails, fix the underlying issue and create a new commit.
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
name: penpot-login
|
||||
description: Connect to Penpot MCP - starts the MCP server, logs into penpot.schweitz.net, and installs/connects the plugin
|
||||
allowed-tools: Read, Bash, mcp__chrome-devtools__new_page, mcp__chrome-devtools__navigate_page, mcp__chrome-devtools__take_snapshot, mcp__chrome-devtools__take_screenshot, mcp__chrome-devtools__click, mcp__chrome-devtools__fill, mcp__chrome-devtools__press_key, mcp__chrome-devtools__list_pages
|
||||
---
|
||||
|
||||
# Penpot MCP Connection
|
||||
|
||||
This skill connects Claude to Penpot by:
|
||||
1. Starting the Penpot MCP server (if not running)
|
||||
2. Logging into penpot.schweitz.net via Authentik
|
||||
3. Installing and connecting the Penpot MCP plugin
|
||||
|
||||
## Step 1: Start MCP Server
|
||||
|
||||
Check if the MCP server is running, start it if not:
|
||||
|
||||
```bash
|
||||
# Check if server is running
|
||||
if ! curl -s http://localhost:9880/mcp > /dev/null 2>&1; then
|
||||
# Start the server in background
|
||||
cd ~/Projects/penpot-mcp && ./start-server.sh &
|
||||
sleep 3 # Wait for server to start
|
||||
fi
|
||||
```
|
||||
|
||||
Server endpoints when running:
|
||||
- MCP Server: http://localhost:9880/mcp
|
||||
- Plugin Server: http://localhost:9879/
|
||||
- WebSocket: ws://localhost:4402
|
||||
|
||||
## Step 2: Login Credentials
|
||||
|
||||
Read credentials from the project root file `claude-authentik-credentials.md`:
|
||||
!`cat claude-authentik-credentials.md`
|
||||
|
||||
## Step 3: Login Flow
|
||||
|
||||
1. **Navigate to Penpot**
|
||||
- Use `mcp__chrome-devtools__new_page` to go to `https://penpot.schweitz.net`
|
||||
|
||||
2. **Check if already logged in**
|
||||
- Take a snapshot
|
||||
- If you see "Projects" heading, you're logged in - skip to Step 4
|
||||
- If you see login page, continue with authentication
|
||||
|
||||
3. **Authenticate via Authentik** (if not logged in)
|
||||
- Click the OpenID button to redirect to Authentik
|
||||
- On auth.schweitz.net, fill username textbox
|
||||
- Click "Log in" button
|
||||
- Fill password textbox
|
||||
- Click "Continue" button
|
||||
- If redirected to Authentik user page instead of Penpot, navigate back to `https://penpot.schweitz.net`
|
||||
|
||||
## Step 4: Open Design File
|
||||
|
||||
1. **Navigate to the Clide project**
|
||||
- Double-click on the TUI file to open the workspace
|
||||
|
||||
## Step 5: Install/Connect Plugin
|
||||
|
||||
1. **Open Plugins menu**
|
||||
- Click the Plugins button (puzzle icon, keyboard shortcut: Cmd+Alt+P)
|
||||
|
||||
2. **Check if plugin is installed**
|
||||
- If "Penpot MCP Plugin" appears under "INSTALLED PLUGINS", click OPEN
|
||||
- Otherwise, install it first:
|
||||
|
||||
3. **Install plugin** (if not installed)
|
||||
- Fill the plugin URL textbox with: `http://localhost:9879/manifest.json`
|
||||
- Click INSTALL
|
||||
- Click ALLOW on the permissions dialog
|
||||
|
||||
4. **Connect to MCP server**
|
||||
- In the plugin UI, click "CONNECT TO MCP SERVER"
|
||||
- Verify it shows "Connected to MCP server"
|
||||
|
||||
## Step 6: Configure Claude Code MCP
|
||||
|
||||
Add the Penpot MCP server to Claude Code (if not already configured):
|
||||
|
||||
```bash
|
||||
claude mcp add penpot -t http http://localhost:9880/mcp
|
||||
```
|
||||
|
||||
**Important**: Use HTTP transport (`-t http`) with the `/mcp` endpoint. Do NOT use SSE transport - it causes "Server not initialized" errors.
|
||||
|
||||
## Step 7: Verify Connection
|
||||
|
||||
After connecting, restart the MCP connection in Claude Code:
|
||||
- Use `/mcp` command to reconnect the penpot server
|
||||
- The Penpot MCP tools (execute_code, export_shape, etc.) will then be available
|
||||
|
||||
Test with:
|
||||
```javascript
|
||||
mcp__penpot__execute_code(code="return penpot.currentPage.name;")
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Keep the Penpot plugin UI window open while using MCP tools
|
||||
- The MCP server must be running for the plugin to connect
|
||||
- If connection fails, check that the server is running on port 9880
|
||||
- Use HTTP transport (`-t http`), NOT SSE transport
|
||||
@@ -1,193 +0,0 @@
|
||||
---
|
||||
name: pql
|
||||
description: >
|
||||
Query and plan against a markdown vault via the pql CLI. Two surfaces:
|
||||
(1) structural queries — frontmatter, wikilinks, tags, headings, Bases,
|
||||
DSL — use when the user asks about vault contents ("which notes…", "find
|
||||
where…", "what tags", "who links to X", "run a Base", "query the vault");
|
||||
(2) planning — decision records, tickets, project status — use when the
|
||||
user asks about decisions, tickets, work items, or project planning
|
||||
("sync decisions", "create a ticket", "what's the plan status", "show
|
||||
D-5", "board"). Requires `pql` on PATH. JSON on stdout; exit 2 = zero
|
||||
matches (not an error).
|
||||
---
|
||||
|
||||
# pql — vault queries + project planning
|
||||
|
||||
`pql` indexes a vault into SQLite and exposes structural queries plus a
|
||||
planning layer for decision records and tickets. One binary, two surfaces.
|
||||
|
||||
## Precondition
|
||||
|
||||
```bash
|
||||
command -v pql
|
||||
```
|
||||
|
||||
If absent, tell the user to install from
|
||||
https://github.com/postmeridiem/pql/releases/latest. Don't install it
|
||||
yourself. Don't fall back to grep unless the user explicitly asks.
|
||||
|
||||
## First touch: learn the vault
|
||||
|
||||
```bash
|
||||
pql schema
|
||||
```
|
||||
|
||||
Returns one row per frontmatter key with observed types and file counts.
|
||||
Run once per session before writing queries.
|
||||
|
||||
---
|
||||
|
||||
## Surface 1: Vault queries
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `pql files [glob]` | List indexed files; optional glob filter |
|
||||
| `pql tags [--sort count]` | Distinct tags with counts |
|
||||
| `pql backlinks <path>` | Files linking TO a path |
|
||||
| `pql outlinks <path>` | Links FROM a file |
|
||||
| `pql meta <path>` | Frontmatter + tags + outlinks + headings for one file |
|
||||
| `pql schema` | Typed frontmatter schema |
|
||||
| `pql base <name>` | Execute an Obsidian .base file |
|
||||
| `pql shell` | Interactive REPL (indexes once, then query per line) |
|
||||
| `pql query "<DSL>"` | SQL-derived DSL for complex queries |
|
||||
| `pql doctor` | Resolved vault/config/DB/index state |
|
||||
|
||||
### DSL examples
|
||||
|
||||
```sql
|
||||
SELECT name, fm.date WHERE fm.type = 'meeting' ORDER BY fm.date DESC LIMIT 10
|
||||
SELECT path WHERE 'project' IN tags ORDER BY path
|
||||
SELECT name, fm.prior_job WHERE fm.type = 'council-member' ORDER BY name
|
||||
```
|
||||
|
||||
Use `--file q.pql` or `--stdin` for long queries. Don't interpolate vault
|
||||
content into the command line.
|
||||
|
||||
### Query cookbook
|
||||
|
||||
- **Files in folder** → `pql files 'sessions/*'`
|
||||
- **Top tags** → `pql tags --sort count --limit 20`
|
||||
- **What links to X?** → `pql backlinks members/vaasa/persona.md`
|
||||
- **Date range** → `pql query "SELECT name, fm.date WHERE fm.date BETWEEN '2024-01-01' AND '2024-12-31'"`
|
||||
- **Run a Base** → `pql base council-sessions`
|
||||
- **Inspect one file** → `pql meta members/vaasa/persona.md --pretty`
|
||||
|
||||
---
|
||||
|
||||
## Surface 2: Planning (decisions + tickets)
|
||||
|
||||
Planning state lives in `<vault>/.pql/pql.db` (user-authored state, not a
|
||||
cache). Decision records come from `decisions/*.md`; tickets are
|
||||
SQLite-native.
|
||||
|
||||
### Decision subcommands
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `pql decisions sync` | Parse decisions/*.md → upsert into pql.db |
|
||||
| `pql decisions validate` | Dry-run parse; exits non-zero on malformed records |
|
||||
| `pql decisions claim <D\|Q\|R> <domain> "title"` | Print next available ID |
|
||||
| `pql decisions list [--type X] [--domain X] [--status X]` | List decisions |
|
||||
| `pql decisions show <id> [--with-refs] [--with-tickets]` | Show with joins |
|
||||
| `pql decisions coverage` | Confirmed decisions without tickets |
|
||||
| `pql decisions refs <id>` | Cross-references involving a decision |
|
||||
|
||||
Always `pql decisions sync` before querying if decisions/*.md may have changed.
|
||||
|
||||
### Ticket subcommands
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `pql ticket new <type> "title" [--decision D-NNN] [--priority P]` | Create (emits T-NNN) |
|
||||
| `pql ticket list [--status S] [--team T] [--assigned A] [--label L]` | List with filters |
|
||||
| `pql ticket show <id> [--with-context] [--with-blockers]` | Show with joins (context = ancestors + decisions + children) |
|
||||
| `pql ticket status <id[,id,...]> <new-status>` | Transition (batch via comma-separated IDs) |
|
||||
| `pql ticket assign <id> <agent>` | Set assignee |
|
||||
| `pql ticket block <id> --by <other>` | Add blocker |
|
||||
| `pql ticket unblock <id> --from <other>` | Remove blocker |
|
||||
| `pql ticket team <id> <team>` | Set team |
|
||||
| `pql ticket label <id> add\|rm <label>` | Manage labels |
|
||||
| `pql ticket board [--team T]` | Kanban board view |
|
||||
|
||||
Ticket types: initiative, epic, story, task, bug.
|
||||
Status flow: backlog → ready → in_progress → review → done (also cancelled).
|
||||
|
||||
### Plan subcommands
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `pql plan status` | Dashboard: decision counts, open Qs, ticket summary, coverage gaps |
|
||||
| `pql plan export [--to FILE]` | Snapshot planning state to JSON (default: `pql-plan.json`) |
|
||||
| `pql plan import [--from FILE]` | Restore planning state from a JSON snapshot |
|
||||
|
||||
### Versioning planning state
|
||||
|
||||
Planning state lives in `pql.db` (gitignored). To version it in git,
|
||||
use `pql plan export` to write a committed JSON snapshot. pql does NOT
|
||||
do this automatically — the user decides when and how to trigger it:
|
||||
|
||||
- Pre-push hook: `.githooks/pre-push` calls `pql plan export && git add pql-plan.json`
|
||||
- Sprint close: a skill or script exports + commits on milestone
|
||||
- Manual: run `pql plan export` before committing when state changed
|
||||
|
||||
On a fresh clone, `pql plan import` restores from the snapshot.
|
||||
|
||||
### Planning cookbook
|
||||
|
||||
- **Sync and list confirmed** → `pql decisions sync && pql decisions list --type confirmed`
|
||||
- **Show with refs** → `pql decisions show D-5 --with-refs --pretty`
|
||||
- **Create ticket** → `pql ticket new task "implement X" --decision D-5`
|
||||
- **Batch close** → `pql ticket status T-1,T-2,T-3 done`
|
||||
- **Coverage gaps** → `pql decisions coverage`
|
||||
- **Dashboard** → `pql plan status --pretty`
|
||||
- **Snapshot for git** → `pql plan export`
|
||||
|
||||
---
|
||||
|
||||
## Output contract (both surfaces)
|
||||
|
||||
- **stdout:** JSON array (default); `--jsonl` for one object/line; `--pretty`; `--limit N`.
|
||||
- **stderr:** JSON diagnostics `{"level":"…","code":"pql.<phase>.<kind>","msg":"…"}`.
|
||||
- **Exit codes:**
|
||||
- `0` — success, ≥1 result
|
||||
- `2` — zero matches (not an error — say "no matches", not "failed")
|
||||
- `64` — bad flag
|
||||
- `65` — parse/compile error (pass stderr back)
|
||||
- `66` — vault/config not found
|
||||
- `69` — unavailable
|
||||
- `70` — internal error
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- Don't pipe to `jq` for simple projections — use `--limit`, `--pretty`, `--jsonl`.
|
||||
- Don't chain `pql files` + `pql meta` — one `pql query` with WHERE.
|
||||
- Don't parse errors — pass stderr diagnostics back directly.
|
||||
- Don't forget `pql decisions sync` before querying decisions.
|
||||
- Don't try to install or upgrade pql — instruct the user if missing.
|
||||
|
||||
## When NOT to use
|
||||
|
||||
- **Body text search** → `grep`/`rg`.
|
||||
- **Reading file contents** → `Read` tool.
|
||||
- **Code structure** → tree-sitter / LSP.
|
||||
- **Modifying vault files** → `Write`/`Edit`. pql doesn't write to vault content.
|
||||
|
||||
## Permissions
|
||||
|
||||
The consuming project's `.claude/settings.json` should allow:
|
||||
|
||||
```json
|
||||
{
|
||||
"permissions": {
|
||||
"allow": ["Bash(pql)", "Bash(pql *)"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Updating the skill
|
||||
|
||||
`pql skill status` reports drift. `pql skill install` writes/updates;
|
||||
`--force` overrides hand-edits. `pql doctor` also surfaces skill state.
|
||||
@@ -22,6 +22,7 @@ This skill bundles four concerns that all surface in widget work:
|
||||
| Token selection per surface | [`references/surface.md`](references/surface.md) | Building a new widget or modifying an existing one — "which token does this need" |
|
||||
| Spacing, alignment, control layout | [`references/geometry.md`](references/geometry.md) | Building tab strips, list items, buttons, anything where icons sit next to text or padded edges |
|
||||
| Phosphor icon usage and codepoints | [`references/icons.md`](references/icons.md) | Adding or referencing an icon |
|
||||
| Full Phosphor glyph table (1512, with codepoints) | [`references/phosphor-glyphs.md`](references/phosphor-glyphs.md) | Picking a specific glyph by name/look — find its codepoint, see if it's already defined |
|
||||
|
||||
Read the reference that matches the question. They cross-reference each
|
||||
other where relevant; you don't need to read all four.
|
||||
@@ -38,6 +39,36 @@ These apply across every reference and every surface:
|
||||
- Typography: `clideFontMono` for code/paths/IDs, `clideFontCaption` for
|
||||
status/section headers, body inherits from `DefaultTextStyle`.
|
||||
|
||||
## Conversation-panel cards (T-305)
|
||||
|
||||
The Claude conversation stream has **three** card categories. They are NOT one
|
||||
shared wrapper widget — each is its own widget; they only share the spacing
|
||||
constants in `lib/widgets/src/clide_card_metrics.dart` (`kClideCardGap`,
|
||||
`kClideCardRadius`, `kClideCardHeaderPadH/V`, `kClideCardCounterSlotWidth`) so
|
||||
the stream reads as one rhythm. Change spacing there, not per-card.
|
||||
|
||||
1. **Dialog cards** — `ConversationCard` with a speaker **side stripe** (you /
|
||||
claude / agent). Prose/attribution; not collapsible.
|
||||
2. **Simple cards** — a single item shown fully open, never collapses (e.g. the
|
||||
image card). Standalone display; no chevron, no status chrome.
|
||||
3. **Collapsibles** — `ClideCollapserCard` (`lib/widgets/`). Every tool use is
|
||||
one, over a list of `1..N` inner item cards (a single tool = a 1-item list;
|
||||
there is no separate single-card path). Rules:
|
||||
- Whole card toggles; chevron hard against the **left** edge, the status tick
|
||||
(spinner/check/cross) hard against the **right** edge, the count in a
|
||||
fixed-width slot just inboard of it.
|
||||
- `color` drives the border + chevron/label tint (per-instance fidelity).
|
||||
- Collapsed ticker echoes the run's **last content line** as the title +
|
||||
count + aggregate status (computed by the caller, bubbled up from items).
|
||||
- Inner item cards are content + their **own** per-item status; they pass
|
||||
`margin: EdgeInsets.zero`-ish (a bottom margin matching the canvas) so the
|
||||
collapser pads the inner canvas **evenly on all sides** — never let an
|
||||
inner card jam under the header or against a frame edge.
|
||||
|
||||
Do NOT pull a dialog card's stripe or a simple card's config into the collapser,
|
||||
and do NOT nest collapsers — inside a run, tools render as the bare inner content
|
||||
card (`_ConversationTurn(collapseTools: false)`).
|
||||
|
||||
## Anti-patterns (cross-cutting)
|
||||
|
||||
- Borrowing another surface's token (`sidebarBackground` for hat bar) — give
|
||||
|
||||
@@ -169,6 +169,26 @@ Container(
|
||||
- Eyeballing alignment without working back from a target margin in
|
||||
pixels. The math matters; see "uniform inner spacing".
|
||||
|
||||
## Ultrawide — clide is an IDE, assume wide screens (T-239)
|
||||
|
||||
The default `flutter_test` surface is 800px, but clide runs on 3440/5120
|
||||
ultrawide constantly. Two consequences:
|
||||
|
||||
- **Right-align with `Expanded`, not a `Spacer` that fights a flex sibling.**
|
||||
A `Row[ left…, Flexible(flex:1), Spacer(), right… ]` splits the free space
|
||||
between the loose flex item and the `Spacer` 50/50 — so the right group is
|
||||
pushed only *half* the free space. The drift is **proportional to width**:
|
||||
invisible at 800–1200px, ~1500px adrift at 3440px. To pin a right group to
|
||||
the edge regardless of width, put the left group in `Expanded(Row[...])`
|
||||
(it absorbs *all* free space; flex items flex within it) and let the right
|
||||
group trail at intrinsic width. See `StatusbarHost` in `lib/app.dart`.
|
||||
- **Test layout at ultrawide, not just the default surface.** Width-sensitive
|
||||
bugs hide at 800px. Drive the surface with `tester.view.physicalSize` —
|
||||
a wide `SizedBox` under the default 800px surface is CLAMPED to 800, so it
|
||||
doesn't actually test wide. Pattern in `test/app_statusbar_test.dart`
|
||||
(`pumpAt`): set `view.physicalSize`, assert key positions at a normal AND an
|
||||
ultrawide width. Audit tracked in T-241.
|
||||
|
||||
## Testing alignment
|
||||
|
||||
When iterating on a control's spacing:
|
||||
|
||||
@@ -5,28 +5,31 @@
|
||||
The app bundles Phosphor Icons (v2.0.8, MIT) as TTF fonts at
|
||||
`assets/fonts/phosphor/` (regular, bold, fill weights).
|
||||
|
||||
**Codepoint reference:** `assets/fonts/phosphor/codepoints.csv` — full
|
||||
mapping of all 1512 icon codepoints to kebab-case and PascalCase
|
||||
names. Read this file to look up any icon by name or codepoint.
|
||||
|
||||
### Adding an icon
|
||||
|
||||
Find the codepoint in `codepoints.csv`, then add a `static const`
|
||||
entry to `PhosphorIcons` in `lib/widgets/src/icons/phosphor.dart`:
|
||||
|
||||
```dart
|
||||
static const arrowClockwise = PhosphorIconPainter(0xe036);
|
||||
```
|
||||
|
||||
Only add icons we actually use — don't bulk-import the full set.
|
||||
**Glyph reference:** [`phosphor-glyphs.md`](phosphor-glyphs.md) — the full
|
||||
1512-glyph table (codepoint · kebab name · Pascal name). Grep it for the exact
|
||||
kebab name; **all 1512 are available** — no per-icon wiring (T-314).
|
||||
|
||||
### Using an icon
|
||||
|
||||
Reference a glyph by its **exact kebab-case name** — no codepoints in feature
|
||||
code; the label→codepoint table lives only in the generated
|
||||
`lib/widgets/src/icons/phosphor_glyphs.g.dart`:
|
||||
|
||||
```dart
|
||||
ClideIcon(PhosphorIcons.arrowClockwise, size: 13)
|
||||
ClideIcon(PhosphorIcons.byName('arrow-clockwise'), size: 13)
|
||||
```
|
||||
|
||||
Or as a `TabContribution` icon field: `icon: PhosphorIcons.lightbulb`.
|
||||
Or as a `TabContribution` icon field: `icon: PhosphorIcons.byName('lightbulb')`.
|
||||
|
||||
- An **unknown name** is a bug — it degrades to the `placeholder` error box so
|
||||
the mistake is visible. `phosphor_glyphs_test` asserts every `byName('…')`
|
||||
literal in `lib/` resolves, recovering the typo check a const would give.
|
||||
- For an **intentional blank** that still reserves the icon's box (alignment),
|
||||
use `const EmptyIconPainter()` — *not* a missing name.
|
||||
- Regenerate the map after a font bump: `dart run tool/gen_phosphor_glyphs.dart`.
|
||||
|
||||
**Bold weight:** pass `family: 'Phosphor-Bold'` to `PhosphorIconPainter`.
|
||||
**Fill weight:** `family: 'Phosphor-Fill'`.
|
||||
|
||||
**Bold weight:** pass `family: 'Phosphor-Bold'` to `PhosphorIconPainter`.
|
||||
**Fill weight:** `family: 'Phosphor-Fill'`.
|
||||
|
||||
@@ -62,6 +62,36 @@ types, priority levels) should NOT add tokens to `SurfaceTokens`. Instead:
|
||||
This keeps the core token surface lean and lets each extension own its
|
||||
palette. The pattern scales to any extension needing domain colors.
|
||||
|
||||
## Named themes are user contracts — never retune their palette (D-69)
|
||||
|
||||
A bundled theme that ships under a recognisable name (`clide`, `midnight`,
|
||||
`paper`, `terminal`, `catppuccin-*`, …) is a **user contract**. Its palette —
|
||||
including syntax tokens, status colours, and `borderHi` — **MUST NOT be
|
||||
retuned to pass a contrast gate**. Users pick Catppuccin because it looks like
|
||||
Catppuccin; quietly darkening a swatch to clear WCAG changes what they signed
|
||||
up for. Don't "fix" the artist's vision.
|
||||
|
||||
When a theme fails the contrast gate ([D-22](../../../governance/decisions/accessibility.md)),
|
||||
the fix is **a sibling `-hc` (high-contrast) theme**, not an edit to the
|
||||
faithful one (D-69):
|
||||
|
||||
- Keep `foo.yaml` pixel-faithful to the source palette.
|
||||
- Add `foo-hc.yaml` — same silhouette, but muted text / status chips / syntax
|
||||
tokens / focus border bumped to clear the strict (`extendedPairs`) gate. Only
|
||||
`-hc`/`-cb` variants are held to the extended set; base themes pass the
|
||||
baseline (`canonicalPairs`) only.
|
||||
- Register both in `main.dart` `_loadBundledThemes` **and** the bundled list in
|
||||
`test/a11y/contrast_test.dart`.
|
||||
|
||||
Mechanics: `canonicalPairs` (baseline, every theme) vs `extendedPairs`
|
||||
(strict, `-hc`/`-cb` only) live in `lib/kernel/src/theme/contrast.dart`. If a
|
||||
faithful palette is so soft it can't clear even the *baseline* chrome pairs,
|
||||
that's a design call — surface it, don't silently retune. (Catppuccin Latte
|
||||
hit exactly this on two chrome pairs.)
|
||||
|
||||
This is the same split VS Code ships: `Default Dark+` plus a separate
|
||||
`Default High Contrast`.
|
||||
|
||||
## Where to look in the codebase
|
||||
|
||||
- Full token list: `lib/kernel/src/theme/tokens.dart`
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the Phosphor glyph reference page for the ui-design skill.
|
||||
|
||||
Mirrors `assets/fonts/phosphor/codepoints.csv` (the full bundled glyph set)
|
||||
into a readable, greppable markdown table at
|
||||
`.claude/skills/ui-design/references/phosphor-glyphs.md`, flagging which
|
||||
glyphs clide already defines in `lib/widgets/src/icons/phosphor.dart`.
|
||||
|
||||
Run from the repo root: python3 .claude/skills/ui-design/scripts/gen-phosphor-glyphs.py
|
||||
"""
|
||||
import csv
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[4]
|
||||
CSV = ROOT / "assets/fonts/phosphor/codepoints.csv"
|
||||
DART = ROOT / "lib/widgets/src/icons/phosphor.dart"
|
||||
OUT = ROOT / ".claude/skills/ui-design/references/phosphor-glyphs.md"
|
||||
|
||||
# Codepoint -> camelCase accessor already defined in PhosphorIcons.
|
||||
defined = {}
|
||||
for m in re.finditer(r"static const (\w+) = PhosphorIconPainter\((0x[0-9a-fA-F]+)\)", DART.read_text()):
|
||||
defined[int(m.group(2), 16)] = m.group(1)
|
||||
|
||||
rows = list(csv.DictReader(CSV.open()))
|
||||
n_def = sum(1 for r in rows if int(r["codepoint"], 16) in defined)
|
||||
|
||||
lines = [
|
||||
"---",
|
||||
"name: phosphor-glyphs",
|
||||
"type: reference",
|
||||
"tags: [icons, phosphor, ui-design]",
|
||||
"---",
|
||||
"",
|
||||
"# Phosphor glyphs — full reference (v2.0.8)",
|
||||
"",
|
||||
f"All **{len(rows)}** glyphs bundled in clide's Phosphor font "
|
||||
"(`assets/fonts/phosphor/`, MIT). Generated from "
|
||||
"`assets/fonts/phosphor/codepoints.csv` — **do not hand-edit**; regenerate with "
|
||||
"`python3 .claude/skills/ui-design/scripts/gen-phosphor-glyphs.py`.",
|
||||
"",
|
||||
f"The **In clide** column flags the **{n_def}** glyphs already wired into "
|
||||
"`PhosphorIcons` (`lib/widgets/src/icons/phosphor.dart`) — reach for those first. "
|
||||
"To use any other glyph, add a one-line `static const` to that class with the "
|
||||
"codepoint below, then `ClideIcon(PhosphorIcons.<name>, size: 13)`. Keep additions "
|
||||
"to icons we actually use — don't bulk-import.",
|
||||
"",
|
||||
"| Codepoint | Name (kebab) | Pascal | In clide |",
|
||||
"|---|---|---|---|",
|
||||
]
|
||||
for r in rows:
|
||||
cp = r["codepoint"]
|
||||
accessor = defined.get(int(cp, 16))
|
||||
in_clide = f"`PhosphorIcons.{accessor}`" if accessor else ""
|
||||
lines.append(f"| `{cp}` | {r['name']} | {r['pascal_name']} | {in_clide} |")
|
||||
|
||||
OUT.write_text("\n".join(lines) + "\n")
|
||||
print(f"wrote {OUT.relative_to(ROOT)} — {len(rows)} glyphs, {n_def} already defined")
|
||||
@@ -50,24 +50,36 @@ user which area to advance.
|
||||
|
||||
### 1b. Build the work landscape
|
||||
|
||||
For each candidate epic or initiative, expand its children:
|
||||
For each candidate epic or initiative, read its full descendant subtree
|
||||
(nested, with the direct parent in `ancestors`) — one call, no client-side
|
||||
flattening (pql ≥ 1.6.0):
|
||||
|
||||
```bash
|
||||
pql ticket show <id> --with-children --pretty
|
||||
pql ticket show <id> --tree --pretty # add --depth N to cap levels
|
||||
```
|
||||
|
||||
Collect every leaf ticket (story/task/bug) underneath. Deduplicate.
|
||||
This replaces the old `--with-children` + hand-rolled status summaries.
|
||||
|
||||
### 1c. Filter to unblocked tickets
|
||||
### 1c. Filter to unblocked, actionable leaves
|
||||
|
||||
For each leaf with status `ready` or `backlog`, check blockers:
|
||||
Don't walk blockers per ticket — pql does it. Compose the `list` filters
|
||||
(pql ≥ 1.6.0): `--leaf` (no children), `--unblocked` (every blocker is
|
||||
`done`/`cancelled`), and `--status` (since `--leaf`/`--unblocked` are
|
||||
structure/blocker-state only and would otherwise include `done` leaves):
|
||||
|
||||
```bash
|
||||
pql ticket show <id> --with-blockers --pretty
|
||||
pql ticket list --under <epic-id> --leaf --unblocked --status backlog --pretty
|
||||
```
|
||||
|
||||
A ticket is **unblocked** if every blocker is `done` or `cancelled`.
|
||||
Drop the rest.
|
||||
`--status` takes a SINGLE value (not a comma list — `backlog,ready` matches
|
||||
nothing and silently returns `[]`). The repo is almost all `backlog`; if a
|
||||
team uses `ready`, run it a second time with `--status ready`.
|
||||
|
||||
That returns exactly the actionable, unblocked leaves under the epic — the
|
||||
multi-result complement to `pql plan whatsnext` (which now also skips blocked
|
||||
tickets, for the single best pick). Note `--unblocked` only means no *ticket*
|
||||
blocker is open; a ticket "blocked on upstream" in prose (e.g. T-158) still
|
||||
shows — read the description before batching.
|
||||
|
||||
### 1d. Rank and group
|
||||
|
||||
@@ -166,12 +178,18 @@ Run all agents in parallel (single message, multiple Agent tool calls).
|
||||
|
||||
For each ticket that came back GAPS, surface ambiguities to the user
|
||||
via AskUserQuestion. After the user resolves, append the resolution to
|
||||
the ticket via pql:
|
||||
the ticket with `pql ticket append` (pql ≥ 1.6.0) — it appends
|
||||
blank-line-separated WITHOUT round-tripping the existing description, so
|
||||
there's no JSON-splicing or read-modify-write:
|
||||
|
||||
```bash
|
||||
pql ticket refine write T-NN '{"description":"<existing body>\n\n---\nRefinement: <resolution>"}'
|
||||
pql ticket append T-NN "Refinement (<date>): <resolution>"
|
||||
# longer notes: pql ticket append T-NN --file note.md (or --stdin)
|
||||
```
|
||||
|
||||
(Use `append`, not `refine write` — the latter replaces the whole
|
||||
description from a JSON patch and forces you to re-send the existing body.)
|
||||
|
||||
If a gap really requires a new D-record (architectural choice, not just
|
||||
detail), flag it. Ask whether to write the D-record now (`pql decisions
|
||||
claim D <domain> "title"` then author the markdown) or defer with a note
|
||||
@@ -199,6 +217,19 @@ Batch transition (comma-separated IDs):
|
||||
pql ticket status T-1,T-2,T-3 in_progress
|
||||
```
|
||||
|
||||
**Then persist it.** Ticket mutations (status here, and any `ticket new` in
|
||||
Step 2) land only in the gitignored `.pql/pql.db`. The post-checkout/post-merge
|
||||
hooks rebuild that DB from the committed changelog on every branch switch — so
|
||||
un-exported changes vanish silently the next time anyone switches branches. After
|
||||
creating or transitioning tickets, always:
|
||||
|
||||
```bash
|
||||
pql plan export # regenerates .pql/changelog/*.sql
|
||||
git add .pql/changelog && git commit # durable; survives rebuild
|
||||
```
|
||||
|
||||
See the [pql skill](../pql/SKILL.md#versioning-planning-state--data-loss-footgun-read-this).
|
||||
|
||||
### 3b. Branch? Default no.
|
||||
|
||||
Solo-dev flow on this repo — work lands directly on `main` (see recent
|
||||
@@ -232,6 +263,10 @@ End with a tight summary:
|
||||
## Anti-patterns
|
||||
|
||||
- Don't skip Step 0 — stale `pql.db` makes the rest of the skill lie.
|
||||
- Don't leave ticket changes un-exported — `pql.db` is gitignored and the
|
||||
post-checkout/post-merge hooks rebuild it from the committed changelog, so a
|
||||
branch switch silently drops un-exported tickets. Always `pql plan export` +
|
||||
commit `.pql/changelog/` after mutating tickets (Step 3a).
|
||||
- Don't activate a batch the user hasn't confirmed.
|
||||
- Don't spawn refinement agents for tickets that have no description — use
|
||||
`pql ticket refine` instead; it's cheaper and writes back through the
|
||||
|
||||
@@ -4,9 +4,54 @@
|
||||
# Install: `make hooks` (points git core.hooksPath at .githooks/).
|
||||
# Bypass: never. If this runs slowly, fix the slow test; don't reach
|
||||
# for --no-verify (git-commit skill forbids it).
|
||||
#
|
||||
# Fast path (T-348): run the full ~2min test suite only when the push touches
|
||||
# lib/ (app + runtime Dart source) or pubspec.* (deps / version). test/,
|
||||
# assets/, docs, and tooling changes ride along with a lib change in practice,
|
||||
# and an otherwise-skipped push is covered by the next one that does touch lib.
|
||||
# The full suite is always available via `make push-check`, and the release CI
|
||||
# runs it forced on a tagged version. So a lib/pubspec-free push runs just the
|
||||
# instant decisions + changelog gates. A state we can't classify (unfetched
|
||||
# remote, new branch) runs the full gate.
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
echo "==> pre-push: make push-check"
|
||||
make push-check
|
||||
z40=0000000000000000000000000000000000000000
|
||||
|
||||
# Collect every file changed across the commits being pushed. git feeds the
|
||||
# hook one line per ref on stdin: <local-ref> <local-sha> <remote-ref> <remote-sha>.
|
||||
changed=""
|
||||
force_full=0
|
||||
while read -r _local_ref local_sha _remote_ref remote_sha; do
|
||||
[[ "$local_sha" == "$z40" ]] && continue # branch deletion — nothing to test
|
||||
if [[ "$remote_sha" == "$z40" ]]; then
|
||||
# New remote branch: diff from its merge-base with main, else play it safe.
|
||||
base="$(git merge-base "$local_sha" origin/main 2>/dev/null || true)"
|
||||
else
|
||||
base="$remote_sha"
|
||||
fi
|
||||
# If we can't resolve a base locally (e.g. the remote advanced and we haven't
|
||||
# fetched its objects), we can't classify the diff — run the full gate.
|
||||
if [[ -z "$base" ]] || ! git cat-file -e "$base^{commit}" 2>/dev/null; then
|
||||
force_full=1
|
||||
break
|
||||
fi
|
||||
changed+=$'\n'"$(git diff --name-only "$base" "$local_sha")"
|
||||
done
|
||||
|
||||
# Run the full gate when lib/ (app + runtime source) or pubspec.* (deps /
|
||||
# version) is touched, or when we couldn't classify above.
|
||||
needs_gate=1
|
||||
if [[ "$force_full" -eq 0 ]]; then
|
||||
trigger_files="$(printf '%s\n' "$changed" | grep -E '^(lib/|pubspec\.)' || true)"
|
||||
[[ -z "$trigger_files" ]] && needs_gate=0
|
||||
fi
|
||||
|
||||
if [[ "$needs_gate" -eq 0 ]]; then
|
||||
echo "==> pre-push: no lib/ or pubspec change — decisions + changelog gates, skipping tests"
|
||||
make decisions-validate changelog-gate
|
||||
else
|
||||
echo "==> pre-push: make push-check"
|
||||
make push-check
|
||||
fi
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
# -- macOS sandbox (machine-specific paths; template is committed) ------
|
||||
/macos/Runner/DebugProfile.entitlements
|
||||
|
||||
# -- clean-house skill run history / skip ledger (machine-local) --------
|
||||
/governance/decisions/.clean-house-state.md
|
||||
|
||||
# -- frame0 wireframe local↔Frame0 id mapping (machine-local) -----------
|
||||
**/*.idmap.json
|
||||
|
||||
# -- Flutter / Dart (single package at repo root) -----------------------
|
||||
/.dart_tool/
|
||||
/.packages
|
||||
@@ -37,6 +43,22 @@ tools/ui/.serve.pid
|
||||
# coverage/ is committed.
|
||||
/coverage/
|
||||
|
||||
# -- Tee'd flutter-test output (see Makefile `make t T=...`) ---------
|
||||
# Lives under test/ so the repo root stays uncluttered.
|
||||
/test/.test-output/
|
||||
|
||||
# -- Build-time stamps generated by `make gen-build-info` -----------
|
||||
# version (from pubspec.yaml), commit (git short SHA), date (UTC).
|
||||
# Regenerated on every make build/run/test target.
|
||||
/lib/src/build_info.g.dart
|
||||
|
||||
# -- Built C `clide` shell client (T-126). Source under ----------
|
||||
# native/clide-cli/ stays in git; the per-platform compiled binary
|
||||
# is built by `make clide-cli`.
|
||||
/native/linux-x64/clide
|
||||
/native/macos-arm64/clide
|
||||
/native/macos-x64/clide
|
||||
|
||||
# -- Test, coverage, profile output ------------------------------------
|
||||
*.test
|
||||
*.out
|
||||
@@ -104,3 +126,14 @@ legacy/**/.coverage.*
|
||||
*.idmap.json
|
||||
.pql/*
|
||||
!.pql/changelog/
|
||||
!.pql/pql-plan.json
|
||||
|
||||
# -- Local build / env artefacts (root-anchored; never tracked) ---------
|
||||
/.coverage
|
||||
/.venv/
|
||||
/dist/
|
||||
/logs/
|
||||
/firebase-debug.log
|
||||
# stray Python trees at the root (the real Dart suite is test/, singular)
|
||||
/clide/
|
||||
/tests/
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
-- Auto-generated by pql init. CREATE TABLE statements
|
||||
-- Auto-generated by migrate-ids (D-26). CREATE TABLE statements
|
||||
-- for the planning schema; per-table dir keeps the changelog
|
||||
-- self-describing per D-15. CREATE TABLE IF NOT EXISTS is
|
||||
-- idempotent so running schema files from each directory in
|
||||
-- replay order is harmless.
|
||||
--
|
||||
-- Importer parses the markers below to detect schema drift
|
||||
-- between the producing pql version and the local one — a
|
||||
-- bumped canonical_version means projection rules changed
|
||||
-- and replay must refuse rather than silently corrupt state.
|
||||
-- pql:created_by: 1.4.26
|
||||
-- pql:canonical_version: 1
|
||||
-- self-describing per D-15.
|
||||
-- pql:created_by: migrate-ids
|
||||
-- pql:canonical_version: 2
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS decisions (
|
||||
@@ -43,14 +36,23 @@ CREATE TABLE IF NOT EXISTS decision_refs (
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
-- Identity split (D-26): a ticket's stable, collision-proof identity is its
|
||||
-- record_id (a locally-generated ULID, planning.NewRecordID); the friendly
|
||||
-- T-NNN label lives in ticket_idmap and may be reconciled. Every structural
|
||||
-- reference (parent, deps, history, labels) targets record_id, so a label
|
||||
-- clash never corrupts the graph — only ticket_idmap needs a relabel.
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id TEXT PRIMARY KEY,
|
||||
record_id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL CHECK(type IN ('initiative','epic','story','task','bug')),
|
||||
parent_id TEXT REFERENCES tickets(id),
|
||||
parent_record_id TEXT REFERENCES tickets(record_id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'backlog'
|
||||
CHECK(status IN ('backlog','ready','in_progress','review','done','cancelled')),
|
||||
-- No CHECK enumeration: the ticket status vocabulary is per-vault
|
||||
-- configurable (ticket_statuses in .pql/config.yaml). Validation lives
|
||||
-- in Go (planning.StatusSet), so adding/renaming statuses needs no
|
||||
-- schema change. The DEFAULT is a harmless fallback — CreateTicket
|
||||
-- always inserts the configured default explicitly.
|
||||
status TEXT NOT NULL DEFAULT 'backlog',
|
||||
priority TEXT DEFAULT 'medium'
|
||||
CHECK(priority IN ('critical','high','medium','low')),
|
||||
assigned_to TEXT,
|
||||
@@ -63,19 +65,33 @@ CREATE TABLE IF NOT EXISTS tickets (
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
-- ticket_idmap maps a record_id to its current friendly label (T-NNN).
|
||||
-- ticket_id is intentionally NOT globally unique: two uncoordinated clones
|
||||
-- can mint the same label, which surfaces as a duplicate-label collision
|
||||
-- (detected at replay) and is fixed with "pql ticket relabel".
|
||||
CREATE TABLE IF NOT EXISTS ticket_idmap (
|
||||
record_id TEXT PRIMARY KEY REFERENCES tickets(record_id),
|
||||
ticket_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_deps (
|
||||
blocker_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
blocked_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
blocker_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
blocked_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (blocker_id, blocked_id)
|
||||
PRIMARY KEY (blocker_record_id, blocked_record_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
field TEXT NOT NULL,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
@@ -89,14 +105,14 @@ CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_labels (
|
||||
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
label TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (ticket_id, label)
|
||||
PRIMARY KEY (ticket_record_id, label)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS meta (
|
||||
@@ -109,6 +125,8 @@ CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_team ON tickets(team);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_decision_ref ON tickets(decision_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_parent ON tickets(parent_record_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_idmap_label ON ticket_idmap(ticket_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_domain ON decisions(domain);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_type ON decisions(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_decision_refs_target ON decision_refs(target_id);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AV8YXV4NYWS2ZQYW', '06FB0TNQM6KY9JVRKF22GGFMXR', '2026-05-18 11:59:28', '2026-05-18 11:59:28', NULL, 'ade2722e88d76b4ea6264545ff9b5398', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AV8YXV4NYWS2ZQYW', '06FB0TNQM78E2NAM4YK3A2AV6R', '2026-05-18 11:59:31', '2026-05-18 11:59:31', NULL, 'd996c0868b309fd7719ab4a8c2e0dd2b', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KY9JVRKF22GGFMXR', '06FB0TNQM78E2NAM4YK3A2AV6R', '2026-05-18 11:59:34', '2026-05-18 11:59:34', NULL, '05db5396eae627dbfae19415bf12802b', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AV8YXV4NYWS2ZQYW', '06FB0TNQM7J40S0A8Q8PC0PDY8', '2026-05-18 11:59:37', '2026-05-18 11:59:37', NULL, '44a606dc211dc172a0b7e45dca353555', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7J40S0A8Q8PC0PDY8', '06FB0TNQM6KEY6593FMW8Y40VC', '2026-05-18 11:59:41', '2026-05-18 11:59:41', NULL, 'a5958fcc4bc52bcba6c5d22119c1152a', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AV8YXV4NYWS2ZQYW', '06FB0TNQM40XWGSN99DBDQDHJR', '2026-05-18 11:59:44', '2026-05-18 11:59:44', NULL, 'fd9d60cea050f1b2a8657664150e977c', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM78E2NAM4YK3A2AV6R', '06FB0TNQM40XWGSN99DBDQDHJR', '2026-05-18 11:59:47', '2026-05-18 11:59:47', NULL, '32e0aec7ec29068519dbd31c8a8cdc80', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AV8YXV4NYWS2ZQYW', '06FB0TNQM5MW2V3QCMWKMY4VQM', '2026-05-18 11:59:51', '2026-05-18 11:59:51', NULL, 'cd33e0846477b82b5cf7e00781783b8c', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM78E2NAM4YK3A2AV6R', '06FB0TNQM5ZSRZARG7SXCC71NM', '2026-05-18 11:59:54', '2026-05-18 11:59:54', NULL, '4ce716dceb22197381bbf6f7f5cb339c', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KEY6593FMW8Y40VC', '06FB0TNQM5ZSRZARG7SXCC71NM', '2026-05-18 11:59:54', '2026-05-18 11:59:54', NULL, '51d502ceb0a41091beb46513618b5b04', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KY9JVRKF22GGFMXR', '06FB0TNQM5ZSRZARG7SXCC71NM', '2026-05-18 11:59:54', '2026-05-18 11:59:54', NULL, '7f4410ea5c072aaac3317a9ed4a99912', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7J40S0A8Q8PC0PDY8', '06FB0TNQM5ZSRZARG7SXCC71NM', '2026-05-18 11:59:54', '2026-05-18 11:59:54', NULL, 'ccd6da9f9358922bc665133b792c65ed', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AV8YXV4NYWS2ZQYW', '06FB0TNQM5ZSRZARG7SXCC71NM', '2026-05-18 11:59:54', '2026-05-18 11:59:54', NULL, 'd89ba28926b6472133096d779a20f079', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6SRMD12DMXCX6N7DM', '06FB0TNQM6E3X5WWRS09GCDGK8', '2026-05-22 15:59:56', '2026-05-22 15:59:56', NULL, '129c875883b4e5a53cdc0683045ace03', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM54N0ZDJRS7G6MP1XC', '06FB0TNQM6E3X5WWRS09GCDGK8', '2026-05-22 15:59:56', '2026-05-22 15:59:56', NULL, '1b4d4dcf6933819d32df98dff4f89d83', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM653J6604KYDK6RT8R', '06FB0TNQM5AVSMKD15KBJYX48W', '2026-05-22 15:59:56', '2026-05-22 15:59:56', NULL, '1c81cdedd045c46a2f1264bcd1067d11', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6E3X5WWRS09GCDGK8', '06FB0TNQM4V1PZ0EK6G60699EM', '2026-05-22 15:59:56', '2026-05-22 15:59:56', NULL, '23b6fb946d9de5e730bd186a9ed350e0', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6E3X5WWRS09GCDGK8', '06FB0TNQM5AVSMKD15KBJYX48W', '2026-05-22 15:59:56', '2026-05-22 15:59:56', NULL, '876ead25db98c91cadc5ea7caa65b513', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4EQAERS4J9X9RAETR', '06FB0TNQM4V1PZ0EK6G60699EM', '2026-05-22 15:59:56', '2026-05-22 15:59:56', NULL, '91e9aa2d0ffd1f32a52bb5cdf019f3fc', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM653J6604KYDK6RT8R', '06FB0TNQM5V9RNW2BGT5GHRTRG', '2026-05-22 15:59:56', '2026-05-22 15:59:56', NULL, 'a8c93af5185bec5604246916460fea08', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4EQAERS4J9X9RAETR', '06FB0TNQM653J6604KYDK6RT8R', '2026-05-22 15:59:56', '2026-05-22 15:59:56', NULL, 'f05aa1ae12d538345bc451d332395b9c', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5KN50C7A723SEKZXG', '06FB0TNQM5V9RNW2BGT5GHRTRG', '2026-05-23 06:08:24', '2026-05-23 06:08:24', NULL, 'a0eacf85ca484d159a3dcc6c170f1760', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM77GWREHKA9K45P020', '06FB0TNQM6YSQYCE096R2SFRXG', '2026-05-23 09:52:40', '2026-05-23 09:52:40', NULL, 'bc99438a38a478190cae7e43c4342526', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM77GWREHKA9K45P020', '06FB0TNQM7BJS5E1JJGSJ9XHG0', '2026-05-23 09:52:43', '2026-05-23 09:52:43', NULL, '2a604702bb0023631d5a98810f695439', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM77GWREHKA9K45P020', '06FB0TNQM4XM8EY708NAM1JK00', '2026-05-23 09:52:45', '2026-05-23 09:52:45', NULL, '4303c0c52bfcc9f3dbd48f784ef1adf8', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AHSN3BXZFFWS0EPM', '06FB0TNQM78W48QX3JZP0QYAVW', '2026-05-24 16:27:29', '2026-05-24 16:27:29', NULL, '01db76f1732be386944e3da80fcdc0b4', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AHSN3BXZFFWS0EPM', '06FB0TNQM7G7MK8MP3BGQC36HG', '2026-05-24 16:27:29', '2026-05-24 16:27:29', NULL, 'c71b201e5cbe1f75ccda37657808e448', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AHSN3BXZFFWS0EPM', '06FB0TNQM7KA37YDBJ89GJ27H4', '2026-05-24 16:27:29', '2026-05-24 16:27:29', NULL, 'c9369a0c50aa82e2afa9a7fa70534b61', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AHSN3BXZFFWS0EPM', '06FB0TNQM48P8T23ZPR0BRK6PR', '2026-05-24 16:27:37', '2026-05-24 16:27:37', NULL, '7f6b0dddd7ba67b4db049f3d69b7c807', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48P8T23ZPR0BRK6PR', '06FB0TNQM654XV2TG4K0RVV1CW', '2026-05-24 16:27:43', '2026-05-24 16:27:43', NULL, 'ad5cfd3ff355cd44017ea0898ea14780', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48P8T23ZPR0BRK6PR', '06FB0TNQM6FMAHXM1XAHHCQC60', '2026-05-24 16:27:46', '2026-05-24 16:27:46', NULL, '7365404994faf148b33ab5ddc83fa01d', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM654XV2TG4K0RVV1CW', '06FB0TNQM6FMAHXM1XAHHCQC60', '2026-05-24 16:27:52', '2026-05-24 16:27:52', NULL, 'e3930f2673b82e4e6e0938450c522946', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48P8T23ZPR0BRK6PR', '06FB0TNQM71N3YMS82BAYD3M9R', '2026-05-24 16:27:56', '2026-05-24 16:27:56', NULL, '856fdd59d24643888245f06c621f40b8', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM654XV2TG4K0RVV1CW', '06FB0TNQM6V6TKRPA106D5SMMM', '2026-05-25 09:16:24', '2026-05-25 09:16:24', NULL, '329210f950241271814a8ed1255aa993', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48P8T23ZPR0BRK6PR', '06FB0TNQM67ET3VQP90WRXVSMG', '2026-05-25 09:16:24', '2026-05-25 09:16:24', NULL, '8a8027ce60a1ec2566c0b32f67f7759b', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4W624VD4JW1GHDHAR', '06FB0TNQM7WE0SD9N4HCP9BB64', '2026-05-25 09:26:48', '2026-05-25 09:26:48', NULL, '388878e0c968776f9bc7a61934dbd3d6', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6YYG4X5R1GX97TJB4', '06FB0TNQM452XPSG99PA60ZZ48', '2026-05-31 18:01:19', '2026-05-31 18:01:19', NULL, '06723eeaf4ac4b3ca32e24df249de904', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
@@ -0,0 +1,28 @@
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM65R8CNFNE1MA5GGR0', '06FB0TNQM4PH1J6ENYT4PJZRHG', '2026-06-01 18:48:57', '2026-06-01 18:48:57', NULL, '1faf4292ceab6cfe3550dfedad554efe', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4PH1J6ENYT4PJZRHG', '06FB0TNQM5REFZXMY8ESWN0Z90', '2026-06-01 18:48:57', '2026-06-01 18:48:57', NULL, 'ca9731f8247e19ee44e99f51b4c33ba2', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6M83TTXHRXESFZY14', '06FB0TNQM5REFZXMY8ESWN0Z90', '2026-06-01 18:48:58', '2026-06-01 18:48:58', NULL, '097bef3723659e681584982688cb242f', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM54WTNQDMYT0AVR4WM', '06FB0TNQM78J2H2S0R79SSSPW8', '2026-06-02 18:13:52', '2026-06-02 18:13:52', NULL, '112f7b091860de0dc7246f2c1b0eea45', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6Y5SET5WGSS2W78F8', '06FB0TNQM758PXMJH16PA3JHFW', '2026-06-02 18:13:52', '2026-06-02 18:13:52', NULL, '497eb0cb7db0b11194031874d6e8d89d', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7171NRB1T82TGP5RG', '06FB0TNQM78J2H2S0R79SSSPW8', '2026-06-02 18:13:52', '2026-06-02 18:13:52', NULL, '5102b2f239c5c18631997cbc1f2d998c', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6EGSD0SB7NYNSP1BM', '06FB0TNQM758PXMJH16PA3JHFW', '2026-06-02 18:13:52', '2026-06-02 18:13:52', NULL, '9d0c7bbff8117a3569cf3b7937228184', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4C2PSN2GVJY9JDSZG', '06FB0TNQM7171NRB1T82TGP5RG', '2026-06-02 18:13:52', '2026-06-02 18:13:52', NULL, 'd935e3d2978a3584efb0767e75737e84', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM45HHRBCRMV2166Y6M', '06FB0TNQM5YYMMKFD5Z8WG9F60', '2026-06-07 08:49:16', '2026-06-07 08:49:16', NULL, '11d94aab7a635da1ffb610aa2ade2061', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM551VZ2NYV6PD0XQRC', '06FB0TNQM4XBRZJ6DEHFWREWC0', '2026-06-07 10:42:46', '2026-06-07 10:42:46', NULL, 'daa1e2dd097721230deca67500c1c00d', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM551VZ2NYV6PD0XQRC', '06FB0TNQM67PDXAFTPPW7B1WDR', '2026-06-07 10:42:50', '2026-06-07 10:42:50', NULL, '50bb0fcfafb5aacdd747bd9fcd6a3ea6', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB234WP4Y6Q16A0HFW8BSXMG', '2026-06-10 11:12:11', '2026-06-10 11:12:11', NULL, 'e0876fe73623c4063849a13f99d552f5', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB234WP4Y6Q16A0HFW8BSXMG', '2026-06-10 11:12:11', '2026-06-10 11:12:14', NULL, 'cd39602b20a0681e3cca2dfee38d1734', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2ESCR4V6V07CRH18FBCDN8', '2026-06-10 11:12:18', '2026-06-10 11:12:18', NULL, 'c74a2c5d562157c24c11c8fa5d04799e', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2ETJQP0CT6X7W3CWZ6NS9G', '2026-06-10 11:12:20', '2026-06-10 11:12:20', NULL, 'cb20764a77d2a15290ed61a186542095', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2EV29HSK6EJ5VF50R87VC4', '2026-06-10 11:12:25', '2026-06-10 11:12:25', NULL, 'dd9434426790edcaa556221f74c431ba', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2G1WD1839Z90AQ5C0BHNV4', '2026-06-10 11:17:25', '2026-06-10 11:17:25', NULL, '5535e3d16bee2a2cdfcbeb84ea3fca99', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5TWC00GW0P3X02HZW', '06FB2TY91VHK7TPKPMZ11EG3TM', '2026-06-10 12:04:53', '2026-06-10 12:04:53', NULL, 'ed3717d8f6467c0a77236eda670efce5', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5TWC00GW0P3X02HZW', '06FB2TY91VHK7TPKPMZ11EG3TM', '2026-06-10 12:04:53', '2026-06-10 12:05:01', '2026-06-10 12:05:01', '137cd047c9eaec01cac955b49fedd839', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DJZDDZ00BSA04B660RS7M', '06FB3DKQQJ583944DG8561VQ3G', '2026-06-10 13:27:04', '2026-06-10 13:27:04', NULL, '73383d1011fbad641395f27271b141e6', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DKQQJ583944DG8561VQ3G', '06FB3DMF20SYFDT6WX2RFBQXKW', '2026-06-10 13:27:04', '2026-06-10 13:27:04', NULL, '45533aa2b124e6547a3804294fcf3aaf', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DJZDDZ00BSA04B660RS7M', '06FB3DQEMTDHF8SV27AKAB8JHW', '2026-06-10 13:27:05', '2026-06-10 13:27:05', NULL, '0eb2faf91e36a4d37a8a3aa97a8edb71', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DKQQJ583944DG8561VQ3G', '06FB3DQEMTDHF8SV27AKAB8JHW', '2026-06-10 13:27:05', '2026-06-10 13:27:05', NULL, 'b329ff8925097a977f528e7e114d6055', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DMF20SYFDT6WX2RFBQXKW', '06FB3DN94MBCTYJW17ZCYVSXE0', '2026-06-10 13:27:08', '2026-06-10 13:27:08', NULL, 'f494f6efad2377e6fc6d0bbf7ae35f07', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DQEMTDHF8SV27AKAB8JHW', '06FB3DN94MBCTYJW17ZCYVSXE0', '2026-06-10 13:27:09', '2026-06-10 13:27:09', NULL, 'e5d02fc3a99106d29a9ce6af0626f68e', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DMF20SYFDT6WX2RFBQXKW', '06FB3DNQZKV20F7YG5PJH8V8SM', '2026-06-10 13:27:09', '2026-06-10 13:27:09', NULL, '1d5185ae9c9676bd70d0e02e3a5e79a1', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DQEMTDHF8SV27AKAB8JHW', '06FB3DNQZKV20F7YG5PJH8V8SM', '2026-06-10 13:27:10', '2026-06-10 13:27:10', NULL, '90dca94aa700c143290b2b1afaca09ed', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DMF20SYFDT6WX2RFBQXKW', '06FB3DP48FS33CQGRDF7EB9GT0', '2026-06-10 13:27:10', '2026-06-10 13:27:10', NULL, '9b9081edc77f0e9a4b689bbc191771db', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||
@@ -1,15 +1,8 @@
|
||||
-- Auto-generated by pql init. CREATE TABLE statements
|
||||
-- Auto-generated by migrate-ids (D-26). CREATE TABLE statements
|
||||
-- for the planning schema; per-table dir keeps the changelog
|
||||
-- self-describing per D-15. CREATE TABLE IF NOT EXISTS is
|
||||
-- idempotent so running schema files from each directory in
|
||||
-- replay order is harmless.
|
||||
--
|
||||
-- Importer parses the markers below to detect schema drift
|
||||
-- between the producing pql version and the local one — a
|
||||
-- bumped canonical_version means projection rules changed
|
||||
-- and replay must refuse rather than silently corrupt state.
|
||||
-- pql:created_by: 1.4.26
|
||||
-- pql:canonical_version: 1
|
||||
-- self-describing per D-15.
|
||||
-- pql:created_by: migrate-ids
|
||||
-- pql:canonical_version: 2
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS decisions (
|
||||
@@ -43,14 +36,23 @@ CREATE TABLE IF NOT EXISTS decision_refs (
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
-- Identity split (D-26): a ticket's stable, collision-proof identity is its
|
||||
-- record_id (a locally-generated ULID, planning.NewRecordID); the friendly
|
||||
-- T-NNN label lives in ticket_idmap and may be reconciled. Every structural
|
||||
-- reference (parent, deps, history, labels) targets record_id, so a label
|
||||
-- clash never corrupts the graph — only ticket_idmap needs a relabel.
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id TEXT PRIMARY KEY,
|
||||
record_id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL CHECK(type IN ('initiative','epic','story','task','bug')),
|
||||
parent_id TEXT REFERENCES tickets(id),
|
||||
parent_record_id TEXT REFERENCES tickets(record_id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'backlog'
|
||||
CHECK(status IN ('backlog','ready','in_progress','review','done','cancelled')),
|
||||
-- No CHECK enumeration: the ticket status vocabulary is per-vault
|
||||
-- configurable (ticket_statuses in .pql/config.yaml). Validation lives
|
||||
-- in Go (planning.StatusSet), so adding/renaming statuses needs no
|
||||
-- schema change. The DEFAULT is a harmless fallback — CreateTicket
|
||||
-- always inserts the configured default explicitly.
|
||||
status TEXT NOT NULL DEFAULT 'backlog',
|
||||
priority TEXT DEFAULT 'medium'
|
||||
CHECK(priority IN ('critical','high','medium','low')),
|
||||
assigned_to TEXT,
|
||||
@@ -63,19 +65,33 @@ CREATE TABLE IF NOT EXISTS tickets (
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
-- ticket_idmap maps a record_id to its current friendly label (T-NNN).
|
||||
-- ticket_id is intentionally NOT globally unique: two uncoordinated clones
|
||||
-- can mint the same label, which surfaces as a duplicate-label collision
|
||||
-- (detected at replay) and is fixed with "pql ticket relabel".
|
||||
CREATE TABLE IF NOT EXISTS ticket_idmap (
|
||||
record_id TEXT PRIMARY KEY REFERENCES tickets(record_id),
|
||||
ticket_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_deps (
|
||||
blocker_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
blocked_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
blocker_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
blocked_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (blocker_id, blocked_id)
|
||||
PRIMARY KEY (blocker_record_id, blocked_record_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
field TEXT NOT NULL,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
@@ -89,14 +105,14 @@ CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_labels (
|
||||
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
label TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (ticket_id, label)
|
||||
PRIMARY KEY (ticket_record_id, label)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS meta (
|
||||
@@ -109,6 +125,8 @@ CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_team ON tickets(team);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_decision_ref ON tickets(decision_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_parent ON tickets(parent_record_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_idmap_label ON ticket_idmap(ticket_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_domain ON decisions(domain);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_type ON decisions(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_decision_refs_target ON decision_refs(target_id);
|
||||
|
||||
@@ -1,483 +1,161 @@
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'backlog', 'ready', NULL, '2026-04-22 09:42:11', '2026-04-22 09:42:11', '2026-04-22 09:42:11', NULL, '66d7c49f6b2ccdd2ef88a635ec717ca2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'ready', 'in_progress', NULL, '2026-04-22 09:42:11', '2026-04-22 09:42:11', '2026-04-22 09:42:11', NULL, '87c83225076b22ecbc7fd150b8ccce5f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:45:21', '2026-04-22 11:45:21', '2026-04-22 11:45:21', NULL, '056f21749ff40c14feeb380b2425bb04', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'review', 'done', NULL, '2026-04-22 11:45:21', '2026-04-22 11:45:21', '2026-04-22 11:45:21', NULL, 'a345d1db748f86fb707b715ce4f45c82', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:12', '2026-04-22 11:46:12', '2026-04-22 11:46:12', NULL, 'd666982c7578a64cff619807467b0d22', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:16', '2026-04-22 11:46:16', '2026-04-22 11:46:16', NULL, 'bf092de792c1dba0a724e6c3ef0a2ca3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:26', '2026-04-22 11:46:26', '2026-04-22 11:46:26', NULL, '6648704125e10ddf9582b0fc8a91cbb8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'review', 'done', NULL, '2026-04-22 11:46:33', '2026-04-22 11:46:33', '2026-04-22 11:46:33', NULL, '28a5fbab4db244d0cc5c48e7e320ec71', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, '8e8f387c6cf81e8db06c212be3c75ff7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'b0d4dd5c1779606c861d1fd778ee9d0f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'review', 'done', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'b93dd369ad1260e6b1f74798544bafd1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'e5b9592394cabd32ce426a3916c27827', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'review', 'done', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '140ed6257373fb7d199c67da8452bb82', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '20cb7969c9c3af6a6deb93ee56a3a8f6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '456c32193f208cb4dcd78d722c8e5308', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '92282e57e514c08df603650c82fc5ad1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'review', 'done', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '2352d517e219af8edf4537f4145b5a9e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '37b7881534e16c2f369cdfb4a25ac511', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '7e4e0f286bd3bf1687cbbd49fca9e1f4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, 'c6ccbb7da6b303b5fd6ef4f5273c7d07', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:52', '2026-04-22 11:46:52', '2026-04-22 11:46:52', NULL, '99d159170d35cb6225a0b9e6f8e5a4f5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '171dab623718a0cb2a4af914c84e0d30', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '206793ce446e6ba302f209cda04dcb78', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '2a311168aab1126eadcb2e23433ba317', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '3098b6064c8b96eb459c5ed947ca97c4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '366f83e624514e442d539f704a0abbb1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '36c5d3bbd5f027fdb114391abdf1dda6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '381444444f3e04024e2293f61c774d1d', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '46d32785bb5113ebcb050845e67ea280', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '5fde942d02640b9df4e75d1d745b2de5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '74dbfcfee3f60d46ba6e910ebb5fed1b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '7d19553f3714932803e565e8ac106220', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '9de91c0384a97d66eaa01e7f8164f389', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'c257d281cc9fc54be99fea49688ff720', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'cf47b1a47c543dabd8fe0c891c535285', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'd34d4be4170d93183c18754a60ea97e1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'db0af7f083e8985026799a9d3334347d', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '1a24e0b9fbc1472aacf17392a9241b99', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '7774f4c92e18371341b7c0332022d29a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '8589b12b48657f3513e801cd96dab11c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '92508b82168bd20d5a69c63a66271c34', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, 'dc2b5b3b5b348409f6d719f05130804b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '0dd7c9716ba7dab383c7890822ba9224', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '23c19fa354da9eb201212b23696da027', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '79e01d297b5cf559d37f2c0ee3661a51', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '88a0fb2513eebbd4627abd854eefb484', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '9ad3112f08548b5896fd2252dda48be0', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'acba548461502b88a483649f181a65da', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'bd986c8233a7a251c0403c4df3ce8511', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'cfb6a1faab0ce219609f44055648cda1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'dabc0f13943decd855d39febfd2a18ae', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'e340923ef96f70f142a0bee868bb8e67', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'f179c71abcd89f7ed9fa781220d2018c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'fae80bffb40c02fdf07691ef18837b14', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:54', '2026-04-22 12:03:54', '2026-04-22 12:03:54', NULL, '1693e0a6448fb98bd03fe0c4c65371fb', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'review', 'done', NULL, '2026-04-22 12:03:54', '2026-04-22 12:03:54', '2026-04-22 12:03:54', NULL, '80d695ac3bfc5fe1f3615013dea3b06a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'ready', 'in_progress', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '33a14771999bc0062268447b4ffadc73', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'backlog', 'ready', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '5a2f1c3d37090ad5791c6a0e8a7aa80a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'ready', 'in_progress', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '7d84a743a1b9e1ce5c50172b89318153', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'backlog', 'ready', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, 'cad02006950fb6fa0d003553437d3bbe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'in_progress', 'review', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '10f0a16fa4e4899c7817dd5c44cc3ffe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'in_progress', 'review', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '2d697a30a6193eacf2a3ddf1661e861c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'review', 'done', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '591bb27186744d023df34105884fa58f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'review', 'done', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '817b9d8e92707eb588cb7f9eae7d424e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-22', 'status', 'backlog', 'cancelled', NULL, '2026-04-22 20:34:17', '2026-04-22 20:34:17', '2026-04-22 20:34:17', NULL, '5460d369f6cdef9c393a37e41481bcb5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '313d4c9dd6444a4573cbe9584ed18f61', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '4630269c56784316b975ca56b10a2337', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '5a8f7e0eda55bc0893c6597400a8d535', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, 'fac6b818dc68490a80ea32255f0ba404', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '0a7137f82fbb9307496b7e4133b15052', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '5aff1ed40d9a68a5b4d59aab0d24dd31', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '931bcaabbd64af0612fbe6eb9a4470d6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, 'c2fca20d28b2a3a63a56eb2841a4bda9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '84e5b99f4fb99f8ae4d189ddaac2de74', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '8c7ba0fd4c8acc6065a24b80ae28d526', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, 'd91c6440f66e683a87be5e61a366cca7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, 'f9253848d9a95ce1823c8ba78a511fc0', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '389d5abb35b3b73ccb38ac8bec3b92c7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '7e714e95f89966a13e436d2fe38e7443', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, 'b1aa8ff1f9b24cdfc9e0bebf21d38b99', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '39b407be88dbb31f530c8074f6bcecc7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '59df9c23d722ccf1c1ac18baee76c785', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '76b48a80abcba29b1047c794fd004234', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '297763c026010189ea0bd870f77a173e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '2cd947436be1c156eb77d233aeed5da2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '38826870354a1bf51bc2ff21f6da0ea3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '5105038efff2e3055213b1b1403b7610', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '67cc6c0934711b6a592e11e5edc6194c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '6e9e7d3ce9f74ed261912b1cb099dcad', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'a4e702776f3b553fb276782e539ebfc7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'd294248ba8ea651e78aeb6fed0457eb4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'da8dc2002a03eaee7fcd5504abee6f60', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '08f09f3bb9f1a0001110716930824e62', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-29', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '10005b4803d743aa21be0223c8a85d5c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '11ac23b546d0e22206af4221062f5912', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-21', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '127404fe79dfb79d0297b85d78235c68', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-23', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '15fb6606af61e11b6960799721e7cdcc', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '19f6209ce4428172fb16941accb13566', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-27', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '212224b7dcc4b991119dc430ae179311', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '2fe8eed15b3ead999a69cdd08df5e14c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '3990623d651c604fec692edb70976d69', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-28', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '44d273b92ad4c93159706c1f17fcb6a7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '46935c5a6d8ef12cf1c2252bd8d30368', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '551ea05a8d0cc28c0ff7010a139e6fe1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '6748fca610fc8a8503d69846231616fe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '7843cfda82ec1c1622c1a201f82acaf1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '822790bbace926feb2372b8665080932', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-22', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '9a1ae5742a1230e60279d9c72f8b0f75', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-42', 'parent_id', NULL, 'T-5', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a414bc8e4d32fa32483228a4e9c7be8c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-24', 'parent_id', NULL, 'T-3', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a80caae1913b8e9a1d5dab980bf0b77b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-40', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a895d51a2e6e8b448684933cf0224162', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-43', 'parent_id', NULL, 'T-3', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ad9a8a85686e33df17ae15631afa1213', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'aedb3c83402245f81cd10f04d15dcf8e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'b6dbb8114cd77c206d3b8bdf054736b9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-26', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'b7b189fe87f61d42e1d0318d23aeb1fe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'c80049d2c236f0d2512355183b766f0f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-25', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ccdf7bec2a75f5829eab4fdd57171622', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-36', 'parent_id', NULL, 'T-7', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'd13ed78c92d33e61e448c9e155adf870', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-41', 'parent_id', NULL, 'T-7', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'eb2a2ca41a57604b40556e81bf6480a9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'f0d27e423d40a998115e10ffd804f791', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'f5c891c9a5f7d5e3f43ded0dc07eb704', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ff609c22f964618c84b7c36988e87ae7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:01:08', '2026-04-24 07:01:08', '2026-04-24 07:01:08', NULL, '6ae864936d48565e880f06e984e9d978', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:01:08', '2026-04-24 07:01:08', '2026-04-24 07:01:08', NULL, 'ebebc38301a28001a09c9e7066c9aeb9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:01:48', '2026-04-24 07:01:48', '2026-04-24 07:01:48', NULL, '3cf128b79fe91447f72f42d8cffce9e1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:01:48', '2026-04-24 07:01:48', '2026-04-24 07:01:48', NULL, '68f57f5e3a1005ca3009f944b8b634d7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:05:40', '2026-04-24 07:05:40', '2026-04-24 07:05:40', NULL, '50aa3e362dbe9682d98f6d5f71ea97b1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:05:50', '2026-04-24 07:05:50', '2026-04-24 07:05:50', NULL, '4cb951c3ea697881d8fa5b0e86cfdf96', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:05:51', '2026-04-24 07:05:51', '2026-04-24 07:05:51', NULL, '7516ffa5c72fa4e7fd067249393c3653', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:06:30', '2026-04-24 07:06:30', '2026-04-24 07:06:30', NULL, '268576bd9559485dd2ac5e7ffd1f1a79', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:06:32', '2026-04-24 07:06:32', '2026-04-24 07:06:32', NULL, 'ef46c3a3db4c4ba50ff1f0e1baa4c0e6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'review', 'done', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '1cd74d47eb547aa0bef71597eaaa2c5b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'in_progress', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '3b8c17d366d1710d03523303c9df83c8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'review', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '7eb9ca56292802301f93d0e2d680835e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, 'a1a837494c047ade786d79edb75296dd', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'done', 'in_progress', NULL, '2026-04-24 07:08:00', '2026-04-24 07:08:00', '2026-04-24 07:08:00', NULL, 'ac745b5254a0e8f085189c23d95acb7e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'done', NULL, '2026-04-24 07:08:29', '2026-04-24 07:08:29', '2026-04-24 07:08:29', NULL, '2adaf942a49bfeeac90aee1527a98444', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'done', 'in_progress', NULL, '2026-04-24 07:08:29', '2026-04-24 07:08:29', '2026-04-24 07:08:29', NULL, 'e0d54ffba34677dc6e687be801ccec05', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'ready', NULL, '2026-04-24 07:08:35', '2026-04-24 07:08:35', '2026-04-24 07:08:35', NULL, '74e6fa2f524c21300467381c2f3ad4e3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:08:35', '2026-04-24 07:08:35', '2026-04-24 07:08:35', NULL, 'fbd19eabf442e6e0de60028c28956b8f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '7d3efe4ccaee7d45d1a01d28144c4f0a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'in_progress', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '914a2f5f5ef0807a6743f8811e71804b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'in_progress', 'ready', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'a1cd74c76fd10c353aa758cea0331c5c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'cancelled', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'cdbe85f80838e781048780db934d0115', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'cancelled', 'backlog', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'f24e8d138172597d7ddcf26672b67d5f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'f9a83fa1b41d56202c57e6a55b240f35', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'done', NULL, '2026-04-24 07:30:15', '2026-04-24 07:30:15', '2026-04-24 07:30:15', NULL, '99c4e5a68463201e7f1b7764eddfd2ae', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'done', 'backlog', NULL, '2026-04-24 07:30:22', '2026-04-24 07:30:22', '2026-04-24 07:30:22', NULL, 'd0274058b130d8003123881b8a410f7f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:08:16', '2026-04-24 08:08:16', '2026-04-24 08:08:16', NULL, '83d70eb5a7f6e7175e0719fdb23243c2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 08:08:16', '2026-04-24 08:08:16', '2026-04-24 08:08:16', NULL, 'b5acf95cb059f0c573b78d755b938da2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:08:23', '2026-04-24 08:08:23', '2026-04-24 08:08:23', NULL, '4ae08b8af459af0679b05cf1f8ac3950', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'ready', NULL, '2026-04-24 08:08:38', '2026-04-24 08:08:38', '2026-04-24 08:08:38', NULL, 'c7e4cabf5d1fccad28f22d498e91193a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:08:40', '2026-04-24 08:08:40', '2026-04-24 08:08:40', NULL, '9b0937bc7eb82785407c367a90365c3a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'review', NULL, '2026-04-24 08:08:44', '2026-04-24 08:08:44', '2026-04-24 08:08:44', NULL, '90587f61d96b12ce53f2a4edc705fdd8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'review', 'in_progress', NULL, '2026-04-24 08:08:48', '2026-04-24 08:08:48', '2026-04-24 08:08:48', NULL, 'd02ac7ee61fc4dc420fb7597a4f326db', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'ready', NULL, '2026-04-24 08:16:15', '2026-04-24 08:16:15', '2026-04-24 08:16:15', NULL, '145613c94b4366bda03fb76c0f0747ce', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 08:16:19', '2026-04-24 08:16:19', '2026-04-24 08:16:19', NULL, '0d8e318e8060aece6e18788cee56f646', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:16:21', '2026-04-24 08:16:21', '2026-04-24 08:16:21', NULL, '3d8635cfdbaf9b0c22dcba9f15f3c1ad', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:16:47', '2026-04-24 08:16:47', '2026-04-24 08:16:47', NULL, '77f873a7a5fff47ce9b974a6999d03b4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-28', 'status', 'backlog', 'in_progress', NULL, '2026-04-24 08:16:56', '2026-04-24 08:16:56', '2026-04-24 08:16:56', NULL, 'e7610d68ec9871746b4a361e33b18f05', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-43', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:17:11', '2026-04-24 08:17:11', '2026-04-24 08:17:11', NULL, '9c3afbbfca207aeb78541dbd9d90e462', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'backlog', 'ready', NULL, '2026-04-22 09:42:11', '2026-04-22 09:42:11', '2026-04-22 09:42:11', NULL, '66d7c49f6b2ccdd2ef88a635ec717ca2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'ready', 'in_progress', NULL, '2026-04-22 09:42:11', '2026-04-22 09:42:11', '2026-04-22 09:42:11', NULL, '87c83225076b22ecbc7fd150b8ccce5f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:45:21', '2026-04-22 11:45:21', '2026-04-22 11:45:21', NULL, '056f21749ff40c14feeb380b2425bb04', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'review', 'done', NULL, '2026-04-22 11:45:21', '2026-04-22 11:45:21', '2026-04-22 11:45:21', NULL, 'a345d1db748f86fb707b715ce4f45c82', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:12', '2026-04-22 11:46:12', '2026-04-22 11:46:12', NULL, 'd666982c7578a64cff619807467b0d22', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:16', '2026-04-22 11:46:16', '2026-04-22 11:46:16', NULL, 'bf092de792c1dba0a724e6c3ef0a2ca3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:26', '2026-04-22 11:46:26', '2026-04-22 11:46:26', NULL, '6648704125e10ddf9582b0fc8a91cbb8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'review', 'done', NULL, '2026-04-22 11:46:33', '2026-04-22 11:46:33', '2026-04-22 11:46:33', NULL, '28a5fbab4db244d0cc5c48e7e320ec71', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, '8e8f387c6cf81e8db06c212be3c75ff7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'b0d4dd5c1779606c861d1fd778ee9d0f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'review', 'done', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'b93dd369ad1260e6b1f74798544bafd1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'e5b9592394cabd32ce426a3916c27827', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'review', 'done', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '140ed6257373fb7d199c67da8452bb82', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '20cb7969c9c3af6a6deb93ee56a3a8f6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '456c32193f208cb4dcd78d722c8e5308', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '92282e57e514c08df603650c82fc5ad1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'review', 'done', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '2352d517e219af8edf4537f4145b5a9e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '37b7881534e16c2f369cdfb4a25ac511', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '7e4e0f286bd3bf1687cbbd49fca9e1f4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, 'c6ccbb7da6b303b5fd6ef4f5273c7d07', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:52', '2026-04-22 11:46:52', '2026-04-22 11:46:52', NULL, '99d159170d35cb6225a0b9e6f8e5a4f5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '171dab623718a0cb2a4af914c84e0d30', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '206793ce446e6ba302f209cda04dcb78', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '2a311168aab1126eadcb2e23433ba317', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '3098b6064c8b96eb459c5ed947ca97c4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '366f83e624514e442d539f704a0abbb1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '36c5d3bbd5f027fdb114391abdf1dda6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '381444444f3e04024e2293f61c774d1d', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '46d32785bb5113ebcb050845e67ea280', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '5fde942d02640b9df4e75d1d745b2de5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '74dbfcfee3f60d46ba6e910ebb5fed1b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '7d19553f3714932803e565e8ac106220', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '9de91c0384a97d66eaa01e7f8164f389', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'c257d281cc9fc54be99fea49688ff720', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'cf47b1a47c543dabd8fe0c891c535285', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'd34d4be4170d93183c18754a60ea97e1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'db0af7f083e8985026799a9d3334347d', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '1a24e0b9fbc1472aacf17392a9241b99', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '7774f4c92e18371341b7c0332022d29a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '8589b12b48657f3513e801cd96dab11c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '92508b82168bd20d5a69c63a66271c34', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, 'dc2b5b3b5b348409f6d719f05130804b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '0dd7c9716ba7dab383c7890822ba9224', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '23c19fa354da9eb201212b23696da027', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '79e01d297b5cf559d37f2c0ee3661a51', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '88a0fb2513eebbd4627abd854eefb484', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '9ad3112f08548b5896fd2252dda48be0', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'acba548461502b88a483649f181a65da', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'bd986c8233a7a251c0403c4df3ce8511', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'cfb6a1faab0ce219609f44055648cda1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'dabc0f13943decd855d39febfd2a18ae', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'e340923ef96f70f142a0bee868bb8e67', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'f179c71abcd89f7ed9fa781220d2018c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'fae80bffb40c02fdf07691ef18837b14', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:54', '2026-04-22 12:03:54', '2026-04-22 12:03:54', NULL, '1693e0a6448fb98bd03fe0c4c65371fb', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'review', 'done', NULL, '2026-04-22 12:03:54', '2026-04-22 12:03:54', '2026-04-22 12:03:54', NULL, '80d695ac3bfc5fe1f3615013dea3b06a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'ready', 'in_progress', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '33a14771999bc0062268447b4ffadc73', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'backlog', 'ready', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '5a2f1c3d37090ad5791c6a0e8a7aa80a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'ready', 'in_progress', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '7d84a743a1b9e1ce5c50172b89318153', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'backlog', 'ready', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, 'cad02006950fb6fa0d003553437d3bbe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'in_progress', 'review', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '10f0a16fa4e4899c7817dd5c44cc3ffe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'in_progress', 'review', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '2d697a30a6193eacf2a3ddf1661e861c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'review', 'done', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '591bb27186744d023df34105884fa58f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'review', 'done', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '817b9d8e92707eb588cb7f9eae7d424e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-22', 'status', 'backlog', 'cancelled', NULL, '2026-04-22 20:34:17', '2026-04-22 20:34:17', '2026-04-22 20:34:17', NULL, '5460d369f6cdef9c393a37e41481bcb5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '313d4c9dd6444a4573cbe9584ed18f61', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '4630269c56784316b975ca56b10a2337', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '5a8f7e0eda55bc0893c6597400a8d535', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, 'fac6b818dc68490a80ea32255f0ba404', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '0a7137f82fbb9307496b7e4133b15052', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '5aff1ed40d9a68a5b4d59aab0d24dd31', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '931bcaabbd64af0612fbe6eb9a4470d6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, 'c2fca20d28b2a3a63a56eb2841a4bda9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '84e5b99f4fb99f8ae4d189ddaac2de74', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '8c7ba0fd4c8acc6065a24b80ae28d526', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, 'd91c6440f66e683a87be5e61a366cca7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, 'f9253848d9a95ce1823c8ba78a511fc0', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '389d5abb35b3b73ccb38ac8bec3b92c7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '7e714e95f89966a13e436d2fe38e7443', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, 'b1aa8ff1f9b24cdfc9e0bebf21d38b99', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '39b407be88dbb31f530c8074f6bcecc7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '59df9c23d722ccf1c1ac18baee76c785', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '76b48a80abcba29b1047c794fd004234', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '297763c026010189ea0bd870f77a173e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '2cd947436be1c156eb77d233aeed5da2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '38826870354a1bf51bc2ff21f6da0ea3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '5105038efff2e3055213b1b1403b7610', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '67cc6c0934711b6a592e11e5edc6194c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '6e9e7d3ce9f74ed261912b1cb099dcad', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'a4e702776f3b553fb276782e539ebfc7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'd294248ba8ea651e78aeb6fed0457eb4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'da8dc2002a03eaee7fcd5504abee6f60', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '08f09f3bb9f1a0001110716930824e62', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-29', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '10005b4803d743aa21be0223c8a85d5c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '11ac23b546d0e22206af4221062f5912', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-21', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '127404fe79dfb79d0297b85d78235c68', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-23', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '15fb6606af61e11b6960799721e7cdcc', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '19f6209ce4428172fb16941accb13566', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-27', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '212224b7dcc4b991119dc430ae179311', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '2fe8eed15b3ead999a69cdd08df5e14c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '3990623d651c604fec692edb70976d69', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-28', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '44d273b92ad4c93159706c1f17fcb6a7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '46935c5a6d8ef12cf1c2252bd8d30368', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '551ea05a8d0cc28c0ff7010a139e6fe1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '6748fca610fc8a8503d69846231616fe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '7843cfda82ec1c1622c1a201f82acaf1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '822790bbace926feb2372b8665080932', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-22', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '9a1ae5742a1230e60279d9c72f8b0f75', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-42', 'parent_id', NULL, 'T-5', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a414bc8e4d32fa32483228a4e9c7be8c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-24', 'parent_id', NULL, 'T-3', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a80caae1913b8e9a1d5dab980bf0b77b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-40', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a895d51a2e6e8b448684933cf0224162', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-43', 'parent_id', NULL, 'T-3', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ad9a8a85686e33df17ae15631afa1213', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'aedb3c83402245f81cd10f04d15dcf8e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'b6dbb8114cd77c206d3b8bdf054736b9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-26', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'b7b189fe87f61d42e1d0318d23aeb1fe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'c80049d2c236f0d2512355183b766f0f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-25', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ccdf7bec2a75f5829eab4fdd57171622', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-36', 'parent_id', NULL, 'T-7', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'd13ed78c92d33e61e448c9e155adf870', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-41', 'parent_id', NULL, 'T-7', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'eb2a2ca41a57604b40556e81bf6480a9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'f0d27e423d40a998115e10ffd804f791', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'f5c891c9a5f7d5e3f43ded0dc07eb704', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ff609c22f964618c84b7c36988e87ae7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:01:08', '2026-04-24 07:01:08', '2026-04-24 07:01:08', NULL, '6ae864936d48565e880f06e984e9d978', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:01:08', '2026-04-24 07:01:08', '2026-04-24 07:01:08', NULL, 'ebebc38301a28001a09c9e7066c9aeb9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:01:48', '2026-04-24 07:01:48', '2026-04-24 07:01:48', NULL, '3cf128b79fe91447f72f42d8cffce9e1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:01:48', '2026-04-24 07:01:48', '2026-04-24 07:01:48', NULL, '68f57f5e3a1005ca3009f944b8b634d7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:05:40', '2026-04-24 07:05:40', '2026-04-24 07:05:40', NULL, '50aa3e362dbe9682d98f6d5f71ea97b1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:05:50', '2026-04-24 07:05:50', '2026-04-24 07:05:50', NULL, '4cb951c3ea697881d8fa5b0e86cfdf96', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:05:51', '2026-04-24 07:05:51', '2026-04-24 07:05:51', NULL, '7516ffa5c72fa4e7fd067249393c3653', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:06:30', '2026-04-24 07:06:30', '2026-04-24 07:06:30', NULL, '268576bd9559485dd2ac5e7ffd1f1a79', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:06:32', '2026-04-24 07:06:32', '2026-04-24 07:06:32', NULL, 'ef46c3a3db4c4ba50ff1f0e1baa4c0e6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'review', 'done', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '1cd74d47eb547aa0bef71597eaaa2c5b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'in_progress', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '3b8c17d366d1710d03523303c9df83c8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'review', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '7eb9ca56292802301f93d0e2d680835e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, 'a1a837494c047ade786d79edb75296dd', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'done', 'in_progress', NULL, '2026-04-24 07:08:00', '2026-04-24 07:08:00', '2026-04-24 07:08:00', NULL, 'ac745b5254a0e8f085189c23d95acb7e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'done', NULL, '2026-04-24 07:08:29', '2026-04-24 07:08:29', '2026-04-24 07:08:29', NULL, '2adaf942a49bfeeac90aee1527a98444', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'done', 'in_progress', NULL, '2026-04-24 07:08:29', '2026-04-24 07:08:29', '2026-04-24 07:08:29', NULL, 'e0d54ffba34677dc6e687be801ccec05', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'ready', NULL, '2026-04-24 07:08:35', '2026-04-24 07:08:35', '2026-04-24 07:08:35', NULL, '74e6fa2f524c21300467381c2f3ad4e3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:08:35', '2026-04-24 07:08:35', '2026-04-24 07:08:35', NULL, 'fbd19eabf442e6e0de60028c28956b8f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '7d3efe4ccaee7d45d1a01d28144c4f0a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'in_progress', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '914a2f5f5ef0807a6743f8811e71804b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'in_progress', 'ready', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'a1cd74c76fd10c353aa758cea0331c5c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'cancelled', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'cdbe85f80838e781048780db934d0115', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'cancelled', 'backlog', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'f24e8d138172597d7ddcf26672b67d5f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'f9a83fa1b41d56202c57e6a55b240f35', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'done', NULL, '2026-04-24 07:30:15', '2026-04-24 07:30:15', '2026-04-24 07:30:15', NULL, '99c4e5a68463201e7f1b7764eddfd2ae', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'done', 'backlog', NULL, '2026-04-24 07:30:22', '2026-04-24 07:30:22', '2026-04-24 07:30:22', NULL, 'd0274058b130d8003123881b8a410f7f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:08:16', '2026-04-24 08:08:16', '2026-04-24 08:08:16', NULL, '83d70eb5a7f6e7175e0719fdb23243c2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 08:08:16', '2026-04-24 08:08:16', '2026-04-24 08:08:16', NULL, 'b5acf95cb059f0c573b78d755b938da2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:08:23', '2026-04-24 08:08:23', '2026-04-24 08:08:23', NULL, '4ae08b8af459af0679b05cf1f8ac3950', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'ready', NULL, '2026-04-24 08:08:38', '2026-04-24 08:08:38', '2026-04-24 08:08:38', NULL, 'c7e4cabf5d1fccad28f22d498e91193a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:08:40', '2026-04-24 08:08:40', '2026-04-24 08:08:40', NULL, '9b0937bc7eb82785407c367a90365c3a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'review', NULL, '2026-04-24 08:08:44', '2026-04-24 08:08:44', '2026-04-24 08:08:44', NULL, '90587f61d96b12ce53f2a4edc705fdd8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'review', 'in_progress', NULL, '2026-04-24 08:08:48', '2026-04-24 08:08:48', '2026-04-24 08:08:48', NULL, 'd02ac7ee61fc4dc420fb7597a4f326db', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'ready', NULL, '2026-04-24 08:16:15', '2026-04-24 08:16:15', '2026-04-24 08:16:15', NULL, '145613c94b4366bda03fb76c0f0747ce', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 08:16:19', '2026-04-24 08:16:19', '2026-04-24 08:16:19', NULL, '0d8e318e8060aece6e18788cee56f646', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:16:21', '2026-04-24 08:16:21', '2026-04-24 08:16:21', NULL, '3d8635cfdbaf9b0c22dcba9f15f3c1ad', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:16:47', '2026-04-24 08:16:47', '2026-04-24 08:16:47', NULL, '77f873a7a5fff47ce9b974a6999d03b4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-28', 'status', 'backlog', 'in_progress', NULL, '2026-04-24 08:16:56', '2026-04-24 08:16:56', '2026-04-24 08:16:56', NULL, 'e7610d68ec9871746b4a361e33b18f05', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-43', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:17:11', '2026-04-24 08:17:11', '2026-04-24 08:17:11', NULL, '9c3afbbfca207aeb78541dbd9d90e462', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'backlog', 'ready', NULL, '2026-04-22 09:42:11', '2026-04-22 09:42:11', '2026-04-22 09:42:11', NULL, '66d7c49f6b2ccdd2ef88a635ec717ca2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'ready', 'in_progress', NULL, '2026-04-22 09:42:11', '2026-04-22 09:42:11', '2026-04-22 09:42:11', NULL, '87c83225076b22ecbc7fd150b8ccce5f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:45:21', '2026-04-22 11:45:21', '2026-04-22 11:45:21', NULL, '056f21749ff40c14feeb380b2425bb04', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'status', 'review', 'done', NULL, '2026-04-22 11:45:21', '2026-04-22 11:45:21', '2026-04-22 11:45:21', NULL, 'a345d1db748f86fb707b715ce4f45c82', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:12', '2026-04-22 11:46:12', '2026-04-22 11:46:12', NULL, 'd666982c7578a64cff619807467b0d22', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:16', '2026-04-22 11:46:16', '2026-04-22 11:46:16', NULL, 'bf092de792c1dba0a724e6c3ef0a2ca3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:26', '2026-04-22 11:46:26', '2026-04-22 11:46:26', NULL, '6648704125e10ddf9582b0fc8a91cbb8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-2', 'status', 'review', 'done', NULL, '2026-04-22 11:46:33', '2026-04-22 11:46:33', '2026-04-22 11:46:33', NULL, '28a5fbab4db244d0cc5c48e7e320ec71', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, '8e8f387c6cf81e8db06c212be3c75ff7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'b0d4dd5c1779606c861d1fd778ee9d0f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'review', 'done', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'b93dd369ad1260e6b1f74798544bafd1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-3', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'e5b9592394cabd32ce426a3916c27827', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'review', 'done', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '140ed6257373fb7d199c67da8452bb82', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '20cb7969c9c3af6a6deb93ee56a3a8f6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '456c32193f208cb4dcd78d722c8e5308', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-4', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '92282e57e514c08df603650c82fc5ad1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'review', 'done', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '2352d517e219af8edf4537f4145b5a9e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '37b7881534e16c2f369cdfb4a25ac511', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '7e4e0f286bd3bf1687cbbd49fca9e1f4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-5', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, 'c6ccbb7da6b303b5fd6ef4f5273c7d07', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:52', '2026-04-22 11:46:52', '2026-04-22 11:46:52', NULL, '99d159170d35cb6225a0b9e6f8e5a4f5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '171dab623718a0cb2a4af914c84e0d30', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '206793ce446e6ba302f209cda04dcb78', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '2a311168aab1126eadcb2e23433ba317', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '3098b6064c8b96eb459c5ed947ca97c4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '366f83e624514e442d539f704a0abbb1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '36c5d3bbd5f027fdb114391abdf1dda6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '381444444f3e04024e2293f61c774d1d', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '46d32785bb5113ebcb050845e67ea280', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '5fde942d02640b9df4e75d1d745b2de5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '74dbfcfee3f60d46ba6e910ebb5fed1b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '7d19553f3714932803e565e8ac106220', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '9de91c0384a97d66eaa01e7f8164f389', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'c257d281cc9fc54be99fea49688ff720', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'cf47b1a47c543dabd8fe0c891c535285', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'd34d4be4170d93183c18754a60ea97e1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'db0af7f083e8985026799a9d3334347d', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '1a24e0b9fbc1472aacf17392a9241b99', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '7774f4c92e18371341b7c0332022d29a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '8589b12b48657f3513e801cd96dab11c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '92508b82168bd20d5a69c63a66271c34', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, 'dc2b5b3b5b348409f6d719f05130804b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '0dd7c9716ba7dab383c7890822ba9224', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '23c19fa354da9eb201212b23696da027', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '79e01d297b5cf559d37f2c0ee3661a51', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '88a0fb2513eebbd4627abd854eefb484', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '9ad3112f08548b5896fd2252dda48be0', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'acba548461502b88a483649f181a65da', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'bd986c8233a7a251c0403c4df3ce8511', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'cfb6a1faab0ce219609f44055648cda1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'dabc0f13943decd855d39febfd2a18ae', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'e340923ef96f70f142a0bee868bb8e67', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'f179c71abcd89f7ed9fa781220d2018c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'fae80bffb40c02fdf07691ef18837b14', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:54', '2026-04-22 12:03:54', '2026-04-22 12:03:54', NULL, '1693e0a6448fb98bd03fe0c4c65371fb', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-6', 'status', 'review', 'done', NULL, '2026-04-22 12:03:54', '2026-04-22 12:03:54', '2026-04-22 12:03:54', NULL, '80d695ac3bfc5fe1f3615013dea3b06a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'ready', 'in_progress', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '33a14771999bc0062268447b4ffadc73', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'backlog', 'ready', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '5a2f1c3d37090ad5791c6a0e8a7aa80a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'ready', 'in_progress', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '7d84a743a1b9e1ce5c50172b89318153', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'backlog', 'ready', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, 'cad02006950fb6fa0d003553437d3bbe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'in_progress', 'review', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '10f0a16fa4e4899c7817dd5c44cc3ffe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'in_progress', 'review', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '2d697a30a6193eacf2a3ddf1661e861c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'status', 'review', 'done', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '591bb27186744d023df34105884fa58f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'status', 'review', 'done', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '817b9d8e92707eb588cb7f9eae7d424e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-22', 'status', 'backlog', 'cancelled', NULL, '2026-04-22 20:34:17', '2026-04-22 20:34:17', '2026-04-22 20:34:17', NULL, '5460d369f6cdef9c393a37e41481bcb5', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '313d4c9dd6444a4573cbe9584ed18f61', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '4630269c56784316b975ca56b10a2337', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '5a8f7e0eda55bc0893c6597400a8d535', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, 'fac6b818dc68490a80ea32255f0ba404', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '0a7137f82fbb9307496b7e4133b15052', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '5aff1ed40d9a68a5b4d59aab0d24dd31', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '931bcaabbd64af0612fbe6eb9a4470d6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, 'c2fca20d28b2a3a63a56eb2841a4bda9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-31', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '84e5b99f4fb99f8ae4d189ddaac2de74', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-34', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '8c7ba0fd4c8acc6065a24b80ae28d526', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-30', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, 'd91c6440f66e683a87be5e61a366cca7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-33', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, 'f9253848d9a95ce1823c8ba78a511fc0', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '389d5abb35b3b73ccb38ac8bec3b92c7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '7e714e95f89966a13e436d2fe38e7443', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, 'b1aa8ff1f9b24cdfc9e0bebf21d38b99', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '39b407be88dbb31f530c8074f6bcecc7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '59df9c23d722ccf1c1ac18baee76c785', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-32', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '76b48a80abcba29b1047c794fd004234', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '297763c026010189ea0bd870f77a173e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '2cd947436be1c156eb77d233aeed5da2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '38826870354a1bf51bc2ff21f6da0ea3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '5105038efff2e3055213b1b1403b7610', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '67cc6c0934711b6a592e11e5edc6194c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '6e9e7d3ce9f74ed261912b1cb099dcad', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'a4e702776f3b553fb276782e539ebfc7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'd294248ba8ea651e78aeb6fed0457eb4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'da8dc2002a03eaee7fcd5504abee6f60', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-1', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '08f09f3bb9f1a0001110716930824e62', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-29', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '10005b4803d743aa21be0223c8a85d5c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '11ac23b546d0e22206af4221062f5912', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-21', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '127404fe79dfb79d0297b85d78235c68', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-23', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '15fb6606af61e11b6960799721e7cdcc', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-38', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '19f6209ce4428172fb16941accb13566', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-27', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '212224b7dcc4b991119dc430ae179311', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-10', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '2fe8eed15b3ead999a69cdd08df5e14c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-15', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '3990623d651c604fec692edb70976d69', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-28', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '44d273b92ad4c93159706c1f17fcb6a7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-19', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '46935c5a6d8ef12cf1c2252bd8d30368', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-37', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '551ea05a8d0cc28c0ff7010a139e6fe1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-14', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '6748fca610fc8a8503d69846231616fe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-20', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '7843cfda82ec1c1622c1a201f82acaf1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-13', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '822790bbace926feb2372b8665080932', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-22', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '9a1ae5742a1230e60279d9c72f8b0f75', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-42', 'parent_id', NULL, 'T-5', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a414bc8e4d32fa32483228a4e9c7be8c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-24', 'parent_id', NULL, 'T-3', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a80caae1913b8e9a1d5dab980bf0b77b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-40', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'a895d51a2e6e8b448684933cf0224162', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-43', 'parent_id', NULL, 'T-3', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ad9a8a85686e33df17ae15631afa1213', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-9', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'aedb3c83402245f81cd10f04d15dcf8e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-16', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'b6dbb8114cd77c206d3b8bdf054736b9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-26', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'b7b189fe87f61d42e1d0318d23aeb1fe', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-11', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'c80049d2c236f0d2512355183b766f0f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-25', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ccdf7bec2a75f5829eab4fdd57171622', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-36', 'parent_id', NULL, 'T-7', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'd13ed78c92d33e61e448c9e155adf870', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-41', 'parent_id', NULL, 'T-7', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'eb2a2ca41a57604b40556e81bf6480a9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-35', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'f0d27e423d40a998115e10ffd804f791', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-39', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'f5c891c9a5f7d5e3f43ded0dc07eb704', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-12', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ff609c22f964618c84b7c36988e87ae7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:01:08', '2026-04-24 07:01:08', '2026-04-24 07:01:08', NULL, '6ae864936d48565e880f06e984e9d978', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:01:08', '2026-04-24 07:01:08', '2026-04-24 07:01:08', NULL, 'ebebc38301a28001a09c9e7066c9aeb9', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:01:48', '2026-04-24 07:01:48', '2026-04-24 07:01:48', NULL, '3cf128b79fe91447f72f42d8cffce9e1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:01:48', '2026-04-24 07:01:48', '2026-04-24 07:01:48', NULL, '68f57f5e3a1005ca3009f944b8b634d7', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:05:40', '2026-04-24 07:05:40', '2026-04-24 07:05:40', NULL, '50aa3e362dbe9682d98f6d5f71ea97b1', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:05:50', '2026-04-24 07:05:50', '2026-04-24 07:05:50', NULL, '4cb951c3ea697881d8fa5b0e86cfdf96', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:05:51', '2026-04-24 07:05:51', '2026-04-24 07:05:51', NULL, '7516ffa5c72fa4e7fd067249393c3653', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:06:30', '2026-04-24 07:06:30', '2026-04-24 07:06:30', NULL, '268576bd9559485dd2ac5e7ffd1f1a79', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:06:32', '2026-04-24 07:06:32', '2026-04-24 07:06:32', NULL, 'ef46c3a3db4c4ba50ff1f0e1baa4c0e6', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'review', 'done', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '1cd74d47eb547aa0bef71597eaaa2c5b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'in_progress', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '3b8c17d366d1710d03523303c9df83c8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'review', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '7eb9ca56292802301f93d0e2d680835e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, 'a1a837494c047ade786d79edb75296dd', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'done', 'in_progress', NULL, '2026-04-24 07:08:00', '2026-04-24 07:08:00', '2026-04-24 07:08:00', NULL, 'ac745b5254a0e8f085189c23d95acb7e', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'done', NULL, '2026-04-24 07:08:29', '2026-04-24 07:08:29', '2026-04-24 07:08:29', NULL, '2adaf942a49bfeeac90aee1527a98444', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'done', 'in_progress', NULL, '2026-04-24 07:08:29', '2026-04-24 07:08:29', '2026-04-24 07:08:29', NULL, 'e0d54ffba34677dc6e687be801ccec05', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'in_progress', 'ready', NULL, '2026-04-24 07:08:35', '2026-04-24 07:08:35', '2026-04-24 07:08:35', NULL, '74e6fa2f524c21300467381c2f3ad4e3', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-17', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:08:35', '2026-04-24 07:08:35', '2026-04-24 07:08:35', NULL, 'fbd19eabf442e6e0de60028c28956b8f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '7d3efe4ccaee7d45d1a01d28144c4f0a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'ready', 'in_progress', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '914a2f5f5ef0807a6743f8811e71804b', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'in_progress', 'ready', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'a1cd74c76fd10c353aa758cea0331c5c', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'cancelled', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'cdbe85f80838e781048780db934d0115', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'cancelled', 'backlog', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'f24e8d138172597d7ddcf26672b67d5f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'f9a83fa1b41d56202c57e6a55b240f35', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'backlog', 'done', NULL, '2026-04-24 07:30:15', '2026-04-24 07:30:15', '2026-04-24 07:30:15', NULL, '99c4e5a68463201e7f1b7764eddfd2ae', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-18', 'status', 'done', 'backlog', NULL, '2026-04-24 07:30:22', '2026-04-24 07:30:22', '2026-04-24 07:30:22', NULL, 'd0274058b130d8003123881b8a410f7f', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:08:16', '2026-04-24 08:08:16', '2026-04-24 08:08:16', NULL, '83d70eb5a7f6e7175e0719fdb23243c2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 08:08:16', '2026-04-24 08:08:16', '2026-04-24 08:08:16', NULL, 'b5acf95cb059f0c573b78d755b938da2', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:08:23', '2026-04-24 08:08:23', '2026-04-24 08:08:23', NULL, '4ae08b8af459af0679b05cf1f8ac3950', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'ready', NULL, '2026-04-24 08:08:38', '2026-04-24 08:08:38', '2026-04-24 08:08:38', NULL, 'c7e4cabf5d1fccad28f22d498e91193a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:08:40', '2026-04-24 08:08:40', '2026-04-24 08:08:40', NULL, '9b0937bc7eb82785407c367a90365c3a', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'review', NULL, '2026-04-24 08:08:44', '2026-04-24 08:08:44', '2026-04-24 08:08:44', NULL, '90587f61d96b12ce53f2a4edc705fdd8', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'review', 'in_progress', NULL, '2026-04-24 08:08:48', '2026-04-24 08:08:48', '2026-04-24 08:08:48', NULL, 'd02ac7ee61fc4dc420fb7597a4f326db', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'in_progress', 'ready', NULL, '2026-04-24 08:16:15', '2026-04-24 08:16:15', '2026-04-24 08:16:15', NULL, '145613c94b4366bda03fb76c0f0747ce', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'backlog', NULL, '2026-04-24 08:16:19', '2026-04-24 08:16:19', '2026-04-24 08:16:19', NULL, '0d8e318e8060aece6e18788cee56f646', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:16:21', '2026-04-24 08:16:21', '2026-04-24 08:16:21', NULL, '3d8635cfdbaf9b0c22dcba9f15f3c1ad', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-63', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:16:47', '2026-04-24 08:16:47', '2026-04-24 08:16:47', NULL, '77f873a7a5fff47ce9b974a6999d03b4', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-28', 'status', 'backlog', 'in_progress', NULL, '2026-04-24 08:16:56', '2026-04-24 08:16:56', '2026-04-24 08:16:56', NULL, 'e7610d68ec9871746b4a361e33b18f05', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-43', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:17:11', '2026-04-24 08:17:11', '2026-04-24 08:17:11', NULL, '9c3afbbfca207aeb78541dbd9d90e462', 1) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM62E3MNW8TBTKSR9GM', 'status', 'backlog', 'ready', NULL, '2026-04-22 09:42:11', '2026-04-22 09:42:11', '2026-04-22 09:42:11', NULL, '3fba5e751d6a8c9a29335cad1550fa0f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM62E3MNW8TBTKSR9GM', 'status', 'ready', 'in_progress', NULL, '2026-04-22 09:42:11', '2026-04-22 09:42:11', '2026-04-22 09:42:11', NULL, 'c4cb4ad45703aa943016359dc46469e8', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM62E3MNW8TBTKSR9GM', 'status', 'review', 'done', NULL, '2026-04-22 11:45:21', '2026-04-22 11:45:21', '2026-04-22 11:45:21', NULL, '146a823b003c92bcd8b8f7e74f2f9f39', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM62E3MNW8TBTKSR9GM', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:45:21', '2026-04-22 11:45:21', '2026-04-22 11:45:21', NULL, 'bbf575b4892c07075dd436e44e65b2cc', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KX7HEQJ65HZ566DG', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:12', '2026-04-22 11:46:12', '2026-04-22 11:46:12', NULL, 'f5088eafcad461a9a4ee4b4df88357d2', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KX7HEQJ65HZ566DG', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:16', '2026-04-22 11:46:16', '2026-04-22 11:46:16', NULL, '556eba472e2de76accea58ee559ce7d2', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KX7HEQJ65HZ566DG', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:26', '2026-04-22 11:46:26', '2026-04-22 11:46:26', NULL, '3a17808cf45fab2b987da0d0cbde98b6', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KX7HEQJ65HZ566DG', 'status', 'review', 'done', NULL, '2026-04-22 11:46:33', '2026-04-22 11:46:33', '2026-04-22 11:46:33', NULL, '2b09b099a97c35e26f35ac37228e9411', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6M1VZ8Z2SZ94F0EQR', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, '0492e38c415358802970cb077447f7d7', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6M1VZ8Z2SZ94F0EQR', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, '1ed839ad770ebbffdf23364dbcb766eb', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6M1VZ8Z2SZ94F0EQR', 'status', 'review', 'done', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, '4f52a76829285f14e71363d162c600e3', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6M1VZ8Z2SZ94F0EQR', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:37', '2026-04-22 11:46:37', '2026-04-22 11:46:37', NULL, 'efda926413d09bbd05a1c88275fc927f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5GSR5WPC2T7CJM8XW', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '0c904f00c761b272e945045651b3c405', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5GSR5WPC2T7CJM8XW', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '0f7b405a869b6c6d17bddd2189c5b52f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5GSR5WPC2T7CJM8XW', 'status', 'review', 'done', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, '46d21523224743c31eb97a3c1e18d2d9', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5GSR5WPC2T7CJM8XW', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:44', '2026-04-22 11:46:44', '2026-04-22 11:46:44', NULL, 'a33b066448c87f7be0542d61dfad5f66', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4XZA5399D2EHYCVFC', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '061a7151be46322c0128980c073356df', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4XZA5399D2EHYCVFC', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '08480dd43012f8f6f37e212767d26be2', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4XZA5399D2EHYCVFC', 'status', 'review', 'done', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '7c004a225b6b82840101504aef4e758c', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4XZA5399D2EHYCVFC', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:46:48', '2026-04-22 11:46:48', '2026-04-22 11:46:48', NULL, '905c9d2df79619009b56009d193aa952', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7DV4ZS010XWZJ9A84', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:46:52', '2026-04-22 11:46:52', '2026-04-22 11:46:52', NULL, '9cafe6c3de8d9f5a3db8321ac7bcbafc', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7YHYTWJGWPFJNHEAR', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '11c89804420370461fbd8fe880758f7e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM79D19C0ZTXAVPKCCG', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '36f16d47e4acf599d2d78933bcc03c77', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6TS48DVFDVJNZ5WGC', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '4162b254ba6ddb4daa8e85e428c5608c', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6TS48DVFDVJNZ5WGC', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '45a71ef6bad527950f92d9323c3edf0a', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4R0WRHSJV1MVCW31M', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '477dc88ff367b8a6bd393711a9dc934b', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7YHYTWJGWPFJNHEAR', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '5d92aa65816538906b0cc1ed5ee87ed3', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7YHYTWJGWPFJNHEAR', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '6b742f381bb7a072b02dfc55db62447f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6TS48DVFDVJNZ5WGC', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '9842cfa577a4dc7eaf6792266bb365be', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4R0WRHSJV1MVCW31M', 'status', 'review', 'done', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, '9dd7f0c00d8c7e3c7a67abaa8412bec9', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM79D19C0ZTXAVPKCCG', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'c557f84b4744487609e7ffb02fd2ed49', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4R0WRHSJV1MVCW31M', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'cd7a68edc46c277c49fa5839990504ad', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM79D19C0ZTXAVPKCCG', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'd387e7910739e3ecc6702b51f0d8b9e4', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM79D19C0ZTXAVPKCCG', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'e1d3d1a9ca033dd1d334d478c8802e32', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7YHYTWJGWPFJNHEAR', 'status', 'in_progress', 'review', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'e4253f407304ff571299945fabe31eee', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4R0WRHSJV1MVCW31M', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'f22005f7ae58243f1fce0b07e6475235', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6TS48DVFDVJNZ5WGC', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:47:11', '2026-04-22 11:47:11', '2026-04-22 11:47:11', NULL, 'feb8a00523b7118d285847f7d88e70d3', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7TW8EJNJ2VP306A20', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, '4bad0ff51702836a574ecb8afb5bd47e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QHBTA4VE6TWQT43R', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, 'e64eeea1f1fcbea7c020191e6c94303d', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7DV4ZS010XWZJ9A84', 'status', 'ready', 'in_progress', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, 'e767c59bd07f4ceb3dabbbbd9a8b25dc', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM59BAJSJ1JESDTPNER', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, 'f32b33eaddf68e8213bfbe698960da29', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM75XRV8AQG2HEQZEJ0', 'status', 'backlog', 'ready', NULL, '2026-04-22 11:52:59', '2026-04-22 11:52:59', '2026-04-22 11:52:59', NULL, 'feb5c5a54317cafb1ca8d77ba93cc81e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM75XRV8AQG2HEQZEJ0', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '08373a38596647969ebd74504544616a', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM75XRV8AQG2HEQZEJ0', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '3a7ee18edc398147113c0ab45b4812eb', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM59BAJSJ1JESDTPNER', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '5adac012531c1b1f400ecb2290782e5b', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QHBTA4VE6TWQT43R', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, '7952677f8c48b6dc6e9c6e05a78e80a8', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QHBTA4VE6TWQT43R', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'a545724b00ae8c2f2b9ea09cb0a22eb0', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM59BAJSJ1JESDTPNER', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'ba68ce26a31c4bc0c56d5dcf1640b6e9', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM59BAJSJ1JESDTPNER', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'c1a7e08caf95080e168df4e611dba0d7', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7TW8EJNJ2VP306A20', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'c97eb916069dd8267e623872edcd7452', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7TW8EJNJ2VP306A20', 'status', 'ready', 'in_progress', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'd520ce983ac294f25f96da3e22fe2257', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM75XRV8AQG2HEQZEJ0', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'db6a409471d1fcf96494fca0cf36b950', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7TW8EJNJ2VP306A20', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'eabc93b76de154cf79976608c0d00603', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QHBTA4VE6TWQT43R', 'status', 'review', 'done', NULL, '2026-04-22 12:03:48', '2026-04-22 12:03:48', '2026-04-22 12:03:48', NULL, 'ed6fc64e88ca1dc9065e72ebd6067e4d', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7DV4ZS010XWZJ9A84', 'status', 'in_progress', 'review', NULL, '2026-04-22 12:03:54', '2026-04-22 12:03:54', '2026-04-22 12:03:54', NULL, '11f427298c60d6f583daa698b0b54e37', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7DV4ZS010XWZJ9A84', 'status', 'review', 'done', NULL, '2026-04-22 12:03:54', '2026-04-22 12:03:54', '2026-04-22 12:03:54', NULL, '8a18fc5ed3fbf30e4abddd0cfe100bff', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JH7WV2RH5DGEA2AC', 'status', 'backlog', 'ready', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, '74a219dafd7350205cac9eeda53552c2', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6VMWWSP4BV8Q11W0M', 'status', 'backlog', 'ready', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, 'e7e1a901f6e799c0eae79c9dc2bb4a33', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JH7WV2RH5DGEA2AC', 'status', 'ready', 'in_progress', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, 'ec16cf36360eb2a7805bffed1a243ecf', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6VMWWSP4BV8Q11W0M', 'status', 'ready', 'in_progress', NULL, '2026-04-22 14:16:39', '2026-04-22 14:16:39', '2026-04-22 14:16:39', NULL, 'f49e07cca55f982a6a07162f55c0c6d8', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6VMWWSP4BV8Q11W0M', 'status', 'in_progress', 'review', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '127e63f3c290f90e7c3aa847afd24923', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JH7WV2RH5DGEA2AC', 'status', 'review', 'done', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '488736bebe779b1281384ce5765a883e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JH7WV2RH5DGEA2AC', 'status', 'in_progress', 'review', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '76d283c661ddda877270447bf9ed3b0b', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6VMWWSP4BV8Q11W0M', 'status', 'review', 'done', NULL, '2026-04-22 14:30:05', '2026-04-22 14:30:05', '2026-04-22 14:30:05', NULL, '8108195e0776ff2e2493720ef80c5b6b', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6PMW57C70KBJVAV10', 'status', 'backlog', 'cancelled', NULL, '2026-04-22 20:34:17', '2026-04-22 20:34:17', '2026-04-22 20:34:17', NULL, '66eac257c948b529c5949d17ca5f1d11', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4VVHNKNXPYD4XWRYW', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, '2031b1358057b8fad868649bb7484500', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6GHHRANK5532VJVJR', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, 'a46451df6d3b67e3bcfbf48bb73c2e12', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM64DC28XV20JJ8KW00', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, 'c397fe7de79cc82035f57a5cdaa2b83d', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5WXV1RKPDK810C3FR', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:38:42', '2026-04-22 20:38:42', '2026-04-22 20:38:42', NULL, 'd471b281adf859311624bb19452d36ba', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5WXV1RKPDK810C3FR', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '154c84814ce1eacfd09580762e35748e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4VVHNKNXPYD4XWRYW', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, '2a3c5b0f2799072f0c593128ac99ddec', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM64DC28XV20JJ8KW00', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, 'e78fe6d9079f625573cf0993bb73a684', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6GHHRANK5532VJVJR', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:38:49', '2026-04-22 20:38:49', '2026-04-22 20:38:49', NULL, 'ffade557a91afb2ce8d223a59c987d15', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6GHHRANK5532VJVJR', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '0ad715f5bfdec4a3c8005f0f4cb857d6', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4VVHNKNXPYD4XWRYW', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '4cd3af18c76884911ac0eff87f93e4c0', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5WXV1RKPDK810C3FR', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '870fda31d4fbd8c39e4a6ed78551a089', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM64DC28XV20JJ8KW00', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:38:51', '2026-04-22 20:38:51', '2026-04-22 20:38:51', NULL, '92206091799f820dd2ae2778a92947fc', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4005W4QV0ZPBMQ02C', 'status', 'in_progress', 'done', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '0798cec4e8ce91238498218a90fc2b4e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4005W4QV0ZPBMQ02C', 'status', 'backlog', 'ready', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '4d677f10ac70a801bc5d691e618dd098', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4005W4QV0ZPBMQ02C', 'status', 'ready', 'in_progress', NULL, '2026-04-22 20:56:58', '2026-04-22 20:56:58', '2026-04-22 20:56:58', NULL, '79fde318878ec91f2cd020be9db706c4', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7MKCHC37G69SJGQ04', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '615893bb4159bca57088c5a8e202e834', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7MKCHC37G69SJGQ04', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, '7350f6584bee9eeadb6a41001f34876b', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7MKCHC37G69SJGQ04', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:02:29', '2026-04-22 21:02:29', '2026-04-22 21:02:29', NULL, 'a4994cbb075a8921e4d0d505d922981c', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KH7VR0BMZV5SSTXC', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '114825918ccc9039ad4653dc5c3b5785', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6JXWYYQ6MT6N8STYM', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '1bc4adcc9db4ab023130198ed8299bb4', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5Q6AWSTKGT0P64TRR', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '2c6e445be22794878bd6784a5973c028', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KH7VR0BMZV5SSTXC', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '307d5b39c6fe3df3d2a620a591d7d5c8', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5Q6AWSTKGT0P64TRR', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '798c86f77cdd2b3a1a337291215b45d6', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6JXWYYQ6MT6N8STYM', 'status', 'backlog', 'ready', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '81141b1ca5d1d6ba526b33054189ebfa', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6JXWYYQ6MT6N8STYM', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '83e47e7c7550ce0f3c724edfa773016f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KH7VR0BMZV5SSTXC', 'status', 'in_progress', 'done', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, '8b6ef7ffc9b872edbe8ccd513f0a239e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5Q6AWSTKGT0P64TRR', 'status', 'ready', 'in_progress', NULL, '2026-04-22 21:16:41', '2026-04-22 21:16:41', '2026-04-22 21:16:41', NULL, 'c040aca52959ae8febda5e0dd54252a2', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QHBTA4VE6TWQT43R', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '007710b30c9e75b030b44dba9fa5f824', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM79D19C0ZTXAVPKCCG', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '0c80d9ff14fd5992b92f0c9b8b9eb647', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6VMWWSP4BV8Q11W0M', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '18e5b09f781d974d4746fa984734872f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM67JSC5RKS6M9182KG', 'parent_id', NULL, 'T-7', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '1fbe35313558f6277b818cff665009a9', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5YG22EV7BFTX5RTPR', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '20d5c46249f5b8810e17914929ec763e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM699HAWC76SHRA7AM4', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '251327841ab9971717b36c2cb79ca299', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7TW8EJNJ2VP306A20', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '2e3916450f98a766aac0c2abc301e6c4', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5E41Q8TX8X55X4MZ0', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '4502258c81a7e75eafd3631e0cdf90b8', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM67ZXPWX71ND4C699C', 'parent_id', NULL, 'T-3', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '4c190ec1739ed8ec68fe694687fa5fe3', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JK0RHMRAH47K8ND4', 'parent_id', NULL, 'T-7', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '4ca23526443704936c352c2842f2f7f8', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5KYAV8TMH3Y3FPRSR', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '50dc074f7f06d51e11fee8d00dd7dd74', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7YHYTWJGWPFJNHEAR', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '6a700a972bd1c1a0729bf27078def56f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5Q6AWSTKGT0P64TRR', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '773e27abfec30eeb016b441c1dc0d8f8', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM75XRV8AQG2HEQZEJ0', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '881064c3f6f63638a754929f1078b011', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '9404bff31ca5c310db47f5c0e11c90ca', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48MDE8ZZ82VWNY994', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '96cf4f78cd06636178ba9a64d6b83f2d', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4R0WRHSJV1MVCW31M', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '9b17afe084710d3e198c7435ee0fb4b6', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6RDM2EKZX132GPV7M', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '9ca8eb0c58afc81c07a7538b8e504ad4', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM66TK8TQ1X5T9GK6V0', 'parent_id', NULL, 'T-5', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, '9f69eaa0855cbea3a8e715b7bb917624', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6PMW57C70KBJVAV10', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'b683b61fdb656a5f6df871190736771f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KH7VR0BMZV5SSTXC', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'c27a712580912bc125013ca9ee3330a1', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4005W4QV0ZPBMQ02C', 'parent_id', NULL, 'T-4', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'cca49092a5f8f1912acd35c168101148', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4M63G18VAQFG6R3XW', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ce9352bab1e00b12f8ff794fca067732', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM62E3MNW8TBTKSR9GM', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'dc8745c0d7b3445b0c78985f1f9995dc', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6TS48DVFDVJNZ5WGC', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'e1831cbdaa2e3ce0000a590874c08a76', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6JXWYYQ6MT6N8STYM', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'e5214fde5b73b4a7165107dc85057ec5', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JH7WV2RH5DGEA2AC', 'parent_id', NULL, 'T-2', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'e6649095b63a9a991df2db00a7ad53ca', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4W194B2421P2SF83R', 'parent_id', NULL, 'T-8', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'ee6e9785f9f760ab2c6b3f689afefad2', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5QHMB46NSHHPVQRF0', 'parent_id', NULL, 'T-3', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'f9b20bf38afe42bfdfd811ab282658ee', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM59BAJSJ1JESDTPNER', 'parent_id', NULL, 'T-6', NULL, '2026-04-23 10:23:49', '2026-04-23 10:23:49', '2026-04-23 10:23:49', NULL, 'fb24ffd20df49eb6febd496a6c1c02bf', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:01:08', '2026-04-24 07:01:08', '2026-04-24 07:01:08', NULL, '728ad0916172fb20a1c4b19d0753e4db', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:01:08', '2026-04-24 07:01:08', '2026-04-24 07:01:08', NULL, 'bfd785a9e40e27979d287ec137ec01d1', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:01:48', '2026-04-24 07:01:48', '2026-04-24 07:01:48', NULL, '3b1ea2037fa8d26c4293a2bd5d0fa007', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:01:48', '2026-04-24 07:01:48', '2026-04-24 07:01:48', NULL, 'd5329907e937e55e00a9d002da5ac6d4', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:05:40', '2026-04-24 07:05:40', '2026-04-24 07:05:40', NULL, '5a372aa4814812aa716d853550fea046', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:05:50', '2026-04-24 07:05:50', '2026-04-24 07:05:50', NULL, 'ba35f95ff2763f26ebccff21a2b826af', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:05:51', '2026-04-24 07:05:51', '2026-04-24 07:05:51', NULL, '4e46180f02c86278deedd9f8f08d4d07', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:06:30', '2026-04-24 07:06:30', '2026-04-24 07:06:30', NULL, '8e985069c3c5eeb6b172f83430458810', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:06:32', '2026-04-24 07:06:32', '2026-04-24 07:06:32', NULL, '66c14397b8af87bceaace31b1dff2853', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'review', 'done', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '5733cc23693ca40190bd8c5063a7a022', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'in_progress', 'review', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, '9669c3ab67cb4477961d2ee3aa799a90', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'ready', 'in_progress', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, 'de6f0dd22f5f0e664793f74429322b8b', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:07:55', '2026-04-24 07:07:55', '2026-04-24 07:07:55', NULL, 'e7b5b2d4e43d09ede3d177528718683a', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'done', 'in_progress', NULL, '2026-04-24 07:08:00', '2026-04-24 07:08:00', '2026-04-24 07:08:00', NULL, '976f8fecced9b5322ed3403b8fbdf0dc', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'in_progress', 'done', NULL, '2026-04-24 07:08:29', '2026-04-24 07:08:29', '2026-04-24 07:08:29', NULL, '281fe02b5f9467e50e0a186fc1afb77c', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'done', 'in_progress', NULL, '2026-04-24 07:08:29', '2026-04-24 07:08:29', '2026-04-24 07:08:29', NULL, 'fd27037a9f94cf0df10016ff2c3b6e78', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'in_progress', 'ready', NULL, '2026-04-24 07:08:35', '2026-04-24 07:08:35', '2026-04-24 07:08:35', NULL, '52ed48f7463c234efeb35fcf45957e8a', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:08:35', '2026-04-24 07:08:35', '2026-04-24 07:08:35', NULL, '8fb22a82912e93b9abff0da33d371093', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'backlog', 'cancelled', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '3e65f78d186b37308165679295b40408', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'cancelled', 'backlog', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '5b81f1c8048cd2b3ef1055ac9b04f958', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'ready', 'backlog', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '7348283db8af7104b475d0d84a0a7157', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'in_progress', 'ready', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '804f233159ce47a2c213778971fc00a1', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'backlog', 'ready', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, '91171fb011294517240bd33360b08119', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'ready', 'in_progress', NULL, '2026-04-24 07:08:59', '2026-04-24 07:08:59', '2026-04-24 07:08:59', NULL, 'd0b87332faf5a67e7e6d36ff9c981c00', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'backlog', 'done', NULL, '2026-04-24 07:30:15', '2026-04-24 07:30:15', '2026-04-24 07:30:15', NULL, 'bf086ebf2d4e6b0a592f8386e916115b', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'status', 'done', 'backlog', NULL, '2026-04-24 07:30:22', '2026-04-24 07:30:22', '2026-04-24 07:30:22', NULL, '8e29783cc997d80b6699bd14537e8613', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'ready', 'backlog', NULL, '2026-04-24 08:08:16', '2026-04-24 08:08:16', '2026-04-24 08:08:16', NULL, '1bc6c7c959742697cd8670771ebeb8d9', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:08:16', '2026-04-24 08:08:16', '2026-04-24 08:08:16', NULL, '51a117e3e688d86d72e33a844dfb6217', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:08:23', '2026-04-24 08:08:23', '2026-04-24 08:08:23', NULL, 'bf1f3a13e6bc7df6adf20b545e6911aa', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'in_progress', 'ready', NULL, '2026-04-24 08:08:38', '2026-04-24 08:08:38', '2026-04-24 08:08:38', NULL, '66d52940767ba7c91f848431ee618ba1', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:08:40', '2026-04-24 08:08:40', '2026-04-24 08:08:40', NULL, '1154f797d2c87e3e5aff6c8407b97f7e', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'in_progress', 'review', NULL, '2026-04-24 08:08:44', '2026-04-24 08:08:44', '2026-04-24 08:08:44', NULL, '99a8c33d31f16025ad3a27305ec703a0', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'review', 'in_progress', NULL, '2026-04-24 08:08:48', '2026-04-24 08:08:48', '2026-04-24 08:08:48', NULL, '9475c2583b2f0d3742f792f5dcffd4aa', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'in_progress', 'ready', NULL, '2026-04-24 08:16:15', '2026-04-24 08:16:15', '2026-04-24 08:16:15', NULL, 'df08122765cc76b0003b6e9ff54426fb', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'ready', 'backlog', NULL, '2026-04-24 08:16:19', '2026-04-24 08:16:19', '2026-04-24 08:16:19', NULL, '4e273f400ffcfbf2bc926ee4e9575fbc', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:16:21', '2026-04-24 08:16:21', '2026-04-24 08:16:21', NULL, '6e750d5ebacc51f2734e94675322e5c4', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'status', 'ready', 'in_progress', NULL, '2026-04-24 08:16:47', '2026-04-24 08:16:47', '2026-04-24 08:16:47', NULL, '9e82a53ad5b5d58037280a6778e335fe', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4M63G18VAQFG6R3XW', 'status', 'backlog', 'in_progress', NULL, '2026-04-24 08:16:56', '2026-04-24 08:16:56', '2026-04-24 08:16:56', NULL, '8a9247ea1de65f4696dc7af64a5d2b7f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM67ZXPWX71ND4C699C', 'status', 'backlog', 'ready', NULL, '2026-04-24 08:17:11', '2026-04-24 08:17:11', '2026-04-24 08:17:11', NULL, '38b265daee03a4df5fb525e48b477388', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
-- Auto-generated by migrate-ids (D-26). CREATE TABLE statements
|
||||
-- for the planning schema; per-table dir keeps the changelog
|
||||
-- self-describing per D-15.
|
||||
-- pql:created_by: migrate-ids
|
||||
-- pql:canonical_version: 2
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS decisions (
|
||||
id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL CHECK(type IN ('confirmed','question','rejected')),
|
||||
domain TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active'
|
||||
CHECK(status IN ('active','superseded','resolved','open')),
|
||||
date TEXT,
|
||||
file_path TEXT NOT NULL,
|
||||
synced_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS decision_refs (
|
||||
source_id TEXT NOT NULL REFERENCES decisions(id) ON DELETE CASCADE,
|
||||
target_id TEXT NOT NULL REFERENCES decisions(id) ON DELETE CASCADE,
|
||||
ref_type TEXT NOT NULL
|
||||
CHECK(ref_type IN ('supersedes','references','resolves','depends_on','amends')),
|
||||
note TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
-- Identity split (D-26): a ticket's stable, collision-proof identity is its
|
||||
-- record_id (a locally-generated ULID, planning.NewRecordID); the friendly
|
||||
-- T-NNN label lives in ticket_idmap and may be reconciled. Every structural
|
||||
-- reference (parent, deps, history, labels) targets record_id, so a label
|
||||
-- clash never corrupts the graph — only ticket_idmap needs a relabel.
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
record_id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL CHECK(type IN ('initiative','epic','story','task','bug')),
|
||||
parent_record_id TEXT REFERENCES tickets(record_id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
-- No CHECK enumeration: the ticket status vocabulary is per-vault
|
||||
-- configurable (ticket_statuses in .pql/config.yaml). Validation lives
|
||||
-- in Go (planning.StatusSet), so adding/renaming statuses needs no
|
||||
-- schema change. The DEFAULT is a harmless fallback — CreateTicket
|
||||
-- always inserts the configured default explicitly.
|
||||
status TEXT NOT NULL DEFAULT 'backlog',
|
||||
priority TEXT DEFAULT 'medium'
|
||||
CHECK(priority IN ('critical','high','medium','low')),
|
||||
assigned_to TEXT,
|
||||
team TEXT,
|
||||
decision_ref TEXT REFERENCES decisions(id),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
-- ticket_idmap maps a record_id to its current friendly label (T-NNN).
|
||||
-- ticket_id is intentionally NOT globally unique: two uncoordinated clones
|
||||
-- can mint the same label, which surfaces as a duplicate-label collision
|
||||
-- (detected at replay) and is fixed with "pql ticket relabel".
|
||||
CREATE TABLE IF NOT EXISTS ticket_idmap (
|
||||
record_id TEXT PRIMARY KEY REFERENCES tickets(record_id),
|
||||
ticket_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_deps (
|
||||
blocker_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
blocked_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (blocker_record_id, blocked_record_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
field TEXT NOT NULL,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
changed_by TEXT,
|
||||
changed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT UNIQUE,
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_labels (
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
label TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (ticket_record_id, label)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS meta (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_team ON tickets(team);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_decision_ref ON tickets(decision_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_parent ON tickets(parent_record_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_idmap_label ON ticket_idmap(ticket_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_domain ON decisions(domain);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_type ON decisions(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_decision_refs_target ON decision_refs(target_id);
|
||||
@@ -0,0 +1,173 @@
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM67ZXPWX71ND4C699C', 'T-43', '2026-04-22 22:01:13', '2026-05-07 19:04:55', NULL, '069521bcf068ff6889cee5251511446c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7SS2V5Z1M1XJGQX0M', 'T-86', '2026-05-06 13:28:58', '2026-05-07 19:04:55', NULL, '07e51e80b6df55240c465d17a9430700', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7P6Q0DG4RG4CBHFJG', 'T-67', '2026-04-24 06:34:16', '2026-05-07 19:04:55', NULL, '0b64bd846488ccf4fce74a6e14629edb', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM460GRBMZMV8E5339M', 'T-77', '2026-05-05 12:58:59', '2026-05-07 19:04:55', NULL, '0cdd07e0992ab8eb76d6945ae1079855', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM79D19C0ZTXAVPKCCG', 'T-12', '2026-04-22 11:47:03', '2026-05-07 19:04:55', NULL, '10055ea4ae6c926286eb81eb35b8ef8f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427FX3GTR3VNNKM40', 'T-63', '2026-04-24 06:34:16', '2026-05-07 19:04:55', NULL, '178d61b166cf7e8f17efc83cdf60c062', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7DV4ZS010XWZJ9A84', 'T-6', '2026-04-22 11:45:41', '2026-05-07 19:04:55', NULL, '17e7bcab28adfbab47be2ad9aa4e4c56', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4W194B2421P2SF83R', 'T-25', '2026-04-22 20:14:24', '2026-05-07 19:04:55', NULL, '1b888a483ec795737d7a1cf71fdbd699', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM77477EN2R5872G95M', 'T-93', '2026-05-07 06:33:47', '2026-05-07 19:04:55', NULL, '1f5f4e360943d2a903cdd7c819bfeaa3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7D8AG5AY1CW58HCB4', 'T-58', '2026-04-23 20:32:06', '2026-05-07 19:04:55', NULL, '253f8fb1458c5394eea856c885f94fff', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4M63G18VAQFG6R3XW', 'T-28', '2026-04-22 20:14:52', '2026-05-07 19:04:55', NULL, '280276cb372707c0fcdbc7de5c68f568', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7CEP425DNV3W7RFPM', 'T-72', '2026-05-05 12:53:05', '2026-05-07 19:04:55', NULL, '2c7cd24dee2e7d21a31f476af233284d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4XZA5399D2EHYCVFC', 'T-5', '2026-04-22 11:45:41', '2026-05-07 19:04:55', NULL, '31b2ad13bb8bcc41e4d21825a232f086', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM79CNBXJ2S3CFQR7VM', 'T-81', '2026-05-05 12:58:59', '2026-05-07 19:04:55', NULL, '32698f648acf611d10fc719f6be28bad', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM76XDA3SZMPWR8WQSC', 'T-62', '2026-04-24 06:34:16', '2026-05-07 19:04:55', NULL, '3439d016919d87e8b4860735a23de1a8', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5GSR5WPC2T7CJM8XW', 'T-4', '2026-04-22 11:45:41', '2026-05-07 19:04:55', NULL, '407e3921ca9e6d5d13e11b7f577ad591', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4PMYC2E1X99HX4MDM', 'T-84', '2026-05-06 10:16:02', '2026-05-07 19:04:55', NULL, '40ce052d36bd0270aa01f9e25c1a944a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5A8DYZ3BCJN1DEY7C', 'T-71', '2026-05-05 12:53:01', '2026-05-07 19:04:55', NULL, '43cfd9109375b0fb6a8f176a0df764a5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48MDE8ZZ82VWNY994', 'T-27', '2026-04-22 20:14:45', '2026-05-07 19:04:55', NULL, '440474d801f1b9bd05e8fbb4254c3817', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6M1VZ8Z2SZ94F0EQR', 'T-3', '2026-04-22 11:45:41', '2026-05-07 19:04:55', NULL, '44d2e24e49ca8555a6b3327f7e954ede', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7AVABFH067XGTTCVM', 'T-76', '2026-05-05 12:58:59', '2026-05-07 19:04:55', NULL, '4a3e92a591238f629d18f9b10087c7f6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5WXV1RKPDK810C3FR', 'T-31', '2026-04-22 20:33:56', '2026-05-07 19:04:55', NULL, '4c423ea86e8173f7fa6edddea9adbadb', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM62E3MNW8TBTKSR9GM', 'T-1', '2026-04-22 09:41:51', '2026-05-07 19:04:55', NULL, '55239329122e1f84e9aeac9cee14a21a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KH7VR0BMZV5SSTXC', 'T-37', '2026-04-22 21:03:40', '2026-05-07 19:04:55', NULL, '55c80e1ce022b96aab6e97e31579c302', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM42RQZVEWTF9CJSRF8', 'T-85', '2026-05-06 13:28:58', '2026-05-07 19:04:55', NULL, '6133918ee124e5502286fa0de06b5bd9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7QREFG3SBG2QPYY7R', 'T-59', '2026-04-23 20:32:06', '2026-05-07 19:04:55', NULL, '64c389eab795606b9d8b5b65877a67bc', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JH7WV2RH5DGEA2AC', 'T-19', '2026-04-22 14:08:40', '2026-05-07 19:04:55', NULL, '65ba2e99100be29287532b3390054401', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5YG22EV7BFTX5RTPR', 'T-26', '2026-04-22 20:14:33', '2026-05-07 19:04:55', NULL, '685c38f471288372eab298a106a06d66', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6T6580D8ABDVTMNZW', 'T-8', '2026-04-22 11:45:41', '2026-05-07 19:04:55', NULL, '6aa0a08f23546d11b37f80d54920c3be', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4VVHNKNXPYD4XWRYW', 'T-30', '2026-04-22 20:33:53', '2026-05-07 19:04:55', NULL, '6d929d2dcc64310a43b21be1a8314258', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4TQEYESK545T8F164', 'T-57', '2026-04-23 20:32:06', '2026-05-07 19:04:55', NULL, '6ff368c440149011af6cc3e2bdfe243f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM79H4MPEWW2TWQ89D0', 'T-46', '2026-04-23 20:27:58', '2026-05-07 19:04:55', NULL, '71f030d09b588292801ae8853accd90c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM70QNVQY1MQGP3A33G', 'T-78', '2026-05-05 12:58:59', '2026-05-07 19:04:55', NULL, '73ecd48d2d9f1cdf8e1bf6e88a8dc812', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM52JT96V5067SM0DMR', 'T-87', '2026-05-06 13:46:07', '2026-05-07 19:04:55', NULL, '740c1dcab17dbdc339750aa8e32a1281', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QKBQY7FPCNX6N28R', 'T-45', '2026-04-23 20:27:28', '2026-05-07 19:04:55', NULL, '7c312190b902337039b21f4743be8908', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5Q6AWSTKGT0P64TRR', 'T-39', '2026-04-22 21:03:40', '2026-05-07 19:04:55', NULL, '825042c21d032181c1fe6d55a29a94f9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM59BAJSJ1JESDTPNER', 'T-15', '2026-04-22 11:47:18', '2026-05-07 19:04:55', NULL, '836e6112c6e1dff23772619499fad245', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7TW8EJNJ2VP306A20', 'T-14', '2026-04-22 11:47:18', '2026-05-07 19:04:55', NULL, '8496d923db0e1fa76e7b787a273e974e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5QHMB46NSHHPVQRF0', 'T-24', '2026-04-22 14:08:40', '2026-05-07 19:04:55', NULL, '8a892f3717f8da2c93f67d3170af014f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5AACHWPFWZDCJ3CPW', 'T-73', '2026-05-05 12:53:21', '2026-05-07 19:04:55', NULL, '8e532488915724b80881c7b2dc227de9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P00AT6KGVZ1EF60C', 'T-18', '2026-04-22 13:26:29', '2026-05-07 19:04:55', NULL, '900d00f28a29207a464017a0bd750f33', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM734YZ060Q63H40EYG', 'T-80', '2026-05-05 12:58:59', '2026-05-07 19:04:55', NULL, '90da3a2c5bd03b314d661362a3919506', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6JXWYYQ6MT6N8STYM', 'T-38', '2026-04-22 21:03:40', '2026-05-07 19:04:55', NULL, '91c3ef42d94e16789915ee6366f7015d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM75XRV8AQG2HEQZEJ0', 'T-16', '2026-04-22 11:47:18', '2026-05-07 19:04:55', NULL, '98bf61f1693e8b8877c2cc03c9fb120d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM67JSC5RKS6M9182KG', 'T-41', '2026-04-22 21:03:40', '2026-05-07 19:04:55', NULL, '9ab8f6ffa933c87dd3153eaea452e902', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM66FTCTWHH9AQTNFKR', 'T-55', '2026-04-23 20:32:06', '2026-05-07 19:04:55', NULL, 'a7b7dc2af0426c8bcbfea6a3023a0aff', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7YHYTWJGWPFJNHEAR', 'T-10', '2026-04-22 11:47:02', '2026-05-07 19:04:55', NULL, 'a8fa7598a624ad83878db046ffa8986f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6TS48DVFDVJNZ5WGC', 'T-11', '2026-04-22 11:47:02', '2026-05-07 19:04:55', NULL, 'af42494291be5910520becc10285e157', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4R0WRHSJV1MVCW31M', 'T-9', '2026-04-22 11:47:02', '2026-05-07 19:04:55', NULL, 'b8d2fca6e4b3882e4cd1ec486924265b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4005W4QV0ZPBMQ02C', 'T-35', '2026-04-22 20:34:06', '2026-05-07 19:04:55', NULL, 'bbbe9cce785ce60416cba671cea2985a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM50DVPFP4WY74RQBB8', 'T-61', '2026-04-24 06:34:16', '2026-05-07 19:04:55', NULL, 'bbf53c20ed761296c7bd54d418b67218', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM50X0DX8XTVZEA5GN8', 'T-60', '2026-04-23 20:32:06', '2026-05-07 19:04:55', NULL, 'bc1502a8c8fe4e44c004f56e9e15b03e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6PMW57C70KBJVAV10', 'T-22', '2026-04-22 14:08:40', '2026-05-07 19:04:55', NULL, 'be0ae61f02897da33e8a9b2d6d1b002e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6GHHRANK5532VJVJR', 'T-34', '2026-04-22 20:34:03', '2026-05-07 19:04:55', NULL, 'c042cabf5814a596e1582a8a85138543', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM606BMYDDGRSJBVPG8', 'T-70', '2026-05-05 12:52:56', '2026-05-07 19:04:55', NULL, 'c04d59ecdcc1ade801ed7f0866a3f00c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM74S58G8XZV1766VD0', 'T-90', '2026-05-06 20:38:14', '2026-05-07 19:04:55', NULL, 'c36ec6ce6ac423fa9983758304139d2b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7CEKQCMZAV751402G', 'T-47', '2026-04-23 20:28:43', '2026-05-07 19:04:55', NULL, 'c5abd99aac954b783b84f3b77d003691', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5EP8B65T0AR52TQFW', 'T-92', '2026-05-07 05:51:48', '2026-05-07 19:04:55', NULL, 'c62020a1cf82eb7ed6bb5635d5f8f0fa', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QHBTA4VE6TWQT43R', 'T-13', '2026-04-22 11:47:18', '2026-05-07 19:04:55', NULL, 'd08af226653f0822b332b640dfcedde5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KX7HEQJ65HZ566DG', 'T-2', '2026-04-22 11:45:26', '2026-05-07 19:04:55', NULL, 'd1b1abb9046e230f581009daf61e7bfd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7Y0DFP66HSP073XHC', 'T-88', '2026-05-06 14:34:35', '2026-05-07 19:04:55', NULL, 'd1ea1e789ff487787076d0ca66670e35', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JZ2AKN74FKMK3MR8', 'T-69', '2026-05-05 12:52:52', '2026-05-07 19:04:55', NULL, 'd2622779cfbab03758ed02949f12fa72', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7MKCHC37G69SJGQ04', 'T-32', '2026-04-22 20:33:58', '2026-05-07 19:04:55', NULL, 'd4125c549e70d4f4d37bb43a4f3ccbd1', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NS39QCDHNVVVT1R4', 'T-17', '2026-04-22 13:26:29', '2026-05-07 19:04:55', NULL, 'd732affde22ee738f13bacb127c094e7', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5X2K66A0MQY2VZ7H4', 'T-83', '2026-05-06 09:17:39', '2026-05-07 19:04:55', NULL, 'da553a2c61adb1795c0ab33867dc6ba3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4Q4XW93E3WA1SD9KW', 'T-75', '2026-05-05 12:58:59', '2026-05-07 19:04:55', NULL, 'db0b7c934e54d06fcf33aaf168a723e4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6YBQP75NQQ4Y9DN9C', 'T-68', '2026-05-05 12:52:48', '2026-05-07 19:04:55', NULL, 'e14345ea70ff466ed3d9b8de161ea556', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6VMWWSP4BV8Q11W0M', 'T-20', '2026-04-22 14:08:40', '2026-05-07 19:04:55', NULL, 'ebf54efaf812fb838959a80b4cdcc527', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM699HAWC76SHRA7AM4', 'T-21', '2026-04-22 14:08:40', '2026-05-07 19:04:55', NULL, 'f217b0ca72240fbbe463d296f250f3b6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM64DC28XV20JJ8KW00', 'T-33', '2026-04-22 20:34:01', '2026-05-07 19:04:55', NULL, 'f7924a89ac74721c9145a511b7a1b4e9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM454GCK8ENRKRA44NW', 'T-49', '2026-04-23 20:31:35', '2026-05-07 19:04:55', NULL, 'f9fb75eb241d8edd62688d7ac3fdd25a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM57AEVK5E0GWVPGAY8', 'T-79', '2026-05-05 12:58:59', '2026-05-07 19:04:55', NULL, 'fe4c71ca925995617dcd4409caaae9d0', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6RDM2EKZX132GPV7M', 'T-40', '2026-04-22 21:03:40', '2026-05-07 19:04:55', NULL, 'fe57a623da7c3fda11455f1d78ba7f4c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7162Z26FDWXG35VH0', 'T-7', '2026-04-22 11:45:41', '2026-05-07 19:04:55', NULL, 'ffa3d6a93f30550da97a3357a0b40c3c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48X0763Z9DZSM0HFR', 'T-94', '2026-05-07 19:08:29', '2026-05-07 19:08:52', NULL, '8087c73fd647b2156a0275ec7ec2ffcd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5WCDTR41VN9P1EGMC', 'T-96', '2026-05-17 17:16:01', '2026-05-17 18:11:18', NULL, 'c44d6bedd77f53875b4fb7f92e8c9895', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7F3V130BQXQH6HDVC', 'T-98', '2026-05-17 18:47:13', '2026-05-17 18:55:57', NULL, 'b2543702670de76b92f98bcb3690901e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7NRKR9J1QTDSP9650', 'T-101', '2026-05-17 18:47:29', '2026-05-17 19:01:39', NULL, '8acbd73697594b2f0686c7e1f01f0b8e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QNZM15V1ZK32YS6G', 'T-102', '2026-05-17 18:47:33', '2026-05-17 19:01:39', NULL, 'c77e3fe23ababa8854065860aaaf7645', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM493QAZNJTJQHYFGCC', 'T-103', '2026-05-17 18:47:39', '2026-05-17 19:18:43', NULL, '34fae6a3452e2f3a6fc76605fb82cd85', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6MP9JD93ZE0CDGFFW', 'T-106', '2026-05-17 18:47:51', '2026-05-17 19:18:43', NULL, '52a5c071f2c90428eba13aaa8f785831', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7WW3008NZRJWJ88HG', 'T-113', '2026-05-17 18:48:24', '2026-05-17 19:18:43', NULL, 'fabd5c2006ae0e13714b1aa12ecd2e41', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6MWAKNB2NE3ZZ0XKW', 'T-110', '2026-05-17 18:48:09', '2026-05-17 19:39:12', NULL, '22d75188f931b18801e8e9015e9863a5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM60QRRNEEWG84VWKXC', 'T-66', '2026-04-24 06:34:16', '2026-05-17 19:39:37', NULL, '8b24910aba2e49638668b03f64006a5a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM42M9RK4399B4F4WSG', 'T-64', '2026-04-24 06:34:16', '2026-05-17 19:39:37', NULL, '9d3e8f627b3095e1db58e047618be710', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM65SSVGN5M36T4QNDC', 'T-117', '2026-05-17 19:28:28', '2026-05-17 19:39:40', NULL, '852007cdda3b405757c088e160798b40', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4CWANNSKNF2X5JEJ4', 'T-100', '2026-05-17 18:47:23', '2026-05-17 19:47:50', NULL, '3edd0e73ac89116392d4aab4e8180c6a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM63H3CXCKB13E3RNHW', 'T-105', '2026-05-17 18:47:47', '2026-05-17 19:56:28', NULL, '0f673c39dc8c450947f34c86e743f8cc', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM40PV7G53JMHHNYPYG', 'T-108', '2026-05-17 18:47:59', '2026-05-17 20:05:38', NULL, 'f023344206a267eefd5d2c453d3483c9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4ZNR15BSANVX07N1R', 'T-112', '2026-05-17 18:48:20', '2026-05-17 20:11:16', NULL, '874a9bbe234acb55dfc20d72a2c5a361', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4W20PVMHX1BBJ4TVW', 'T-118', '2026-05-17 20:51:47', '2026-05-17 20:53:41', NULL, 'b8fbc53c7092e7b29a869f0247cd0e38', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7FG92QKNFJSCAP888', 'T-114', '2026-05-17 18:48:28', '2026-05-18 07:04:38', NULL, '2c16b22992624d00be14801c105350c9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7Y9KHNVRZ41VYW1YM', 'T-111', '2026-05-17 18:48:14', '2026-05-18 07:43:45', NULL, '1c12b7ea3d82ff2bc1e5680690ee0746', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5W67PPRVA8SAJXHFG', 'T-104', '2026-05-17 18:47:42', '2026-05-18 07:53:10', NULL, 'ec883c703bad46f757d38057c71336d3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4TTAC53CQYWDQ91H0', 'T-109', '2026-05-17 18:48:03', '2026-05-18 08:07:23', NULL, 'ed187720e362af1ff3f8cae3fd0ac828', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM45DS16DX0NKMN9Q3G', 'T-121', '2026-05-18 08:07:23', '2026-05-18 08:22:19', NULL, 'ec1cbea6674c6fb5d365510587c082ec', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM614M7ES54V3SETAQM', 'T-115', '2026-05-17 19:10:21', '2026-05-18 09:30:59', NULL, '640c8f8e5a67cdb44eaabd65bf21fc04', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4RJ9K19P5AX14RHRR', 'T-123', '2026-05-18 10:29:02', '2026-05-18 10:29:02', NULL, '255468e265f300a234e8456d3a0abf51', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7FXS713CKYDZ284NC', 'T-107', '2026-05-17 18:47:55', '2026-05-18 10:30:30', NULL, '4bc77250919cd4cef2fb1bfd3438a7c6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4H5C2Z6GCBCQ85QGR', 'T-116', '2026-05-17 19:14:31', '2026-05-18 11:22:46', NULL, '972bf9cfde6de9ff37cb5cb1dcc5a2dd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM53N56Z60B4SQ5VTRG', 'T-74', '2026-05-05 12:53:22', '2026-05-18 11:51:40', NULL, '7b71c20018634be865ab843900b93eff', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AV8YXV4NYWS2ZQYW', 'T-124', '2026-05-18 11:58:47', '2026-05-18 12:51:08', NULL, 'b272f7b62db06897d71bcee86b0aef6e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KY9JVRKF22GGFMXR', 'T-125', '2026-05-18 11:58:52', '2026-05-18 15:51:09', NULL, '801eba2049b8dc046ab81f731d0caef1', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM78E2NAM4YK3A2AV6R', 'T-126', '2026-05-18 11:58:57', '2026-05-18 16:04:29', NULL, '6bc6a6b1f009225521ace82a6616244c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7J40S0A8Q8PC0PDY8', 'T-127', '2026-05-18 11:59:02', '2026-05-19 10:03:26', NULL, '0e2e8e90772e11cfad16ee7805a58f7e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KEY6593FMW8Y40VC', 'T-128', '2026-05-18 11:59:06', '2026-05-19 10:06:46', NULL, '9cf26209ff598d91824d59f791b34938', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM40XWGSN99DBDQDHJR', 'T-129', '2026-05-18 11:59:11', '2026-05-19 12:19:51', NULL, '618bff52db127ed5655c78c4e09f3078', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5MW2V3QCMWKMY4VQM', 'T-130', '2026-05-18 11:59:17', '2026-05-19 12:33:52', NULL, 'ab53ef037d31eb5a977cf6fe3280e126', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5ZSRZARG7SXCC71NM', 'T-131', '2026-05-18 11:59:23', '2026-05-19 13:14:41', NULL, '7339bc2840f028e45e798e8bd8c9c337', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM76Q97D8R01CRTTA9R', 'T-99', '2026-05-17 18:47:18', '2026-05-19 13:14:41', NULL, 'ff7b3f5bbb802d081c305d32b3330163', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM54DENVAR46RXCCQQM', 'T-119', '2026-05-18 07:43:50', '2026-05-20 07:00:06', NULL, 'd2732a32ffbc29ddca7f9eacad2d5570', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM47ARMYK2H24KJ0DAG', 'T-120', '2026-05-18 07:53:08', '2026-05-20 16:02:35', NULL, 'fdab02033af4c3f6df800ddf3bc78198', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM56CF9W94FSFDSBKGW', 'T-97', '2026-05-17 18:47:08', '2026-05-20 16:02:46', NULL, '84c961d85f80b8ba21fc227f7d191948', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6TNXTJCQBH9VADC4M', 'T-89', '2026-05-06 20:36:42', '2026-05-22 13:37:46', NULL, '7197034866b3116490b8633fd18ff01f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4HGK6KR0314P2M32M', 'T-91', '2026-05-06 20:49:47', '2026-05-22 13:37:46', NULL, 'c1d096207b720c77ac05b1950dce1bcd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM54N0ZDJRS7G6MP1XC', 'T-136', '2026-05-22 15:59:34', '2026-05-22 17:37:23', NULL, '24eae70edc48f10e227710ee77e94fb6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM54G017Y29APWJTCN0', 'T-133', '2026-05-22 15:50:06', '2026-05-22 17:37:23', NULL, 'a4886b596be68f26bcf01315f2eba159', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6SRMD12DMXCX6N7DM', 'T-135', '2026-05-22 15:59:34', '2026-05-22 17:37:23', NULL, 'e18b6b13a86b6202afb93b4bae32c824', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4EQAERS4J9X9RAETR', 'T-134', '2026-05-22 15:59:34', '2026-05-22 17:55:11', NULL, '99fdef2c143698cd8dee33e5903015f5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6E3X5WWRS09GCDGK8', 'T-137', '2026-05-22 15:59:34', '2026-05-22 18:14:32', NULL, '0ae6c36aab9db692afc7b9de9d83b70b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4V1PZ0EK6G60699EM', 'T-138', '2026-05-22 15:59:34', '2026-05-22 21:49:09', NULL, '006e8c34ed9058294d4144ed2fd853ba', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427ZFSTWJ21EBK5FG', 'T-142', '2026-05-22 22:06:59', '2026-05-22 22:18:02', NULL, '93352b90d5b8532dca21609a5e6dee2c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6YWAC8SSGQBK31T98', 'T-143', '2026-05-22 22:31:22', '2026-05-22 22:36:09', NULL, 'd585cc1215d301735c9e207e08193e6e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4BV0CXWCT04Z81EG0', 'T-144', '2026-05-22 22:40:42', '2026-05-22 22:43:45', NULL, 'b8e257b9abde9da0c38fc15cb7d4ee41', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM653J6604KYDK6RT8R', 'T-139', '2026-05-22 15:59:34', '2026-05-22 23:16:28', NULL, 'e43a27d4464ce09179016a65dc702a57', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5AVSMKD15KBJYX48W', 'T-140', '2026-05-22 15:59:34', '2026-05-22 23:31:46', NULL, '2e962cad8956dff339a9c3028ff8c83a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM558PCHET90BBSHGTC', 'T-146', '2026-05-23 06:19:51', '2026-05-23 06:30:47', NULL, 'd575cf325d7b347a88ca11f516b1b715', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM47FEEMHPE8J5VVBKM', 'T-147', '2026-05-23 07:03:18', '2026-05-23 07:06:54', NULL, '2b5e6b5f67000bea0097ea86986124b6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4GDS1R8H2TW12WX7G', 'T-149', '2026-05-23 07:14:36', '2026-05-23 07:27:32', NULL, '1ffa4dc9299a04293bfa473036ed8e08', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5KN50C7A723SEKZXG', 'T-145', '2026-05-23 06:07:59', '2026-05-23 08:43:19', NULL, 'e688de6397028cc53baef88f4af4db5c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6F9XA7F2BX9P18BR4', 'T-150', '2026-05-23 09:04:40', '2026-05-23 09:33:03', NULL, '6103463406709c57456e06226486a739', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM77GWREHKA9K45P020', 'T-151', '2026-05-23 09:52:14', '2026-05-23 10:20:12', NULL, '1ee4e62532962562e7a57316eedf0d04', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7BJS5E1JJGSJ9XHG0', 'T-153', '2026-05-23 09:52:27', '2026-05-23 10:35:56', NULL, '872e6c5e27120a76b24f7c033286071e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6YSQYCE096R2SFRXG', 'T-152', '2026-05-23 09:52:20', '2026-05-23 10:43:52', NULL, '9ba6f625fd14bb95a31902b699e821e6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5B47G9GG22N9KBNEG', 'T-156', '2026-05-23 11:15:08', '2026-05-23 12:05:19', NULL, '35180e547e94880bbe7c9f73b8b28e2c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4XM8EY708NAM1JK00', 'T-154', '2026-05-23 09:52:32', '2026-05-23 14:50:17', NULL, '45a2e199fca3eb06e286f98d5c8c2228', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5R9Q3T7CBMTHM2GFR', 'T-155', '2026-05-23 11:06:50', '2026-05-23 14:50:17', NULL, '578a38cb9982309cac40cd0a214d7839', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6CYT7NW4WE8ZP1TXR', 'T-148', '2026-05-23 07:03:18', '2026-05-23 15:05:29', NULL, 'edd29e766c59c65f2ca3c83700ed96f0', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5V9RNW2BGT5GHRTRG', 'T-141', '2026-05-22 15:59:34', '2026-05-23 20:49:44', NULL, 'c95d953ec43d43dde07d94192342c3a9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QZ3JJF1704AV5JCW', 'T-157', '2026-05-23 20:48:31', '2026-05-23 20:57:07', NULL, '96ab8af2d5b2835a37c23b1af6c25be1', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6KD2DKTGHC6CF1MJ4', 'T-159', '2026-05-24 08:53:13', '2026-05-24 08:54:30', NULL, '6d88f03a4c6268aefb7ab51411e6970f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7SPRJZ519RZK1ZPAW', 'T-161', '2026-05-24 09:24:02', '2026-05-24 09:45:07', NULL, 'e5ad416566fa75f3caf1ccf085aa2506', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5932T486Z1F1M6X6W', 'T-173', '2026-05-24 16:45:40', '2026-05-24 17:10:07', NULL, '0d564a0dc8adc327e3f9460c254499fa', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AHSN3BXZFFWS0EPM', 'T-165', '2026-05-24 16:26:15', '2026-05-25 06:29:25', NULL, '879dcc70eadbc3d29a1e6f81ee0e86d4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7KA37YDBJ89GJ27H4', 'T-166', '2026-05-24 16:26:23', '2026-05-25 06:31:08', NULL, '6a6a0ba97d6331ac3441060ec28197db', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7K30Y9WAYZ3D330YR', 'T-175', '2026-05-25 07:11:09', '2026-05-25 07:25:02', NULL, '1d7a94fb62083c7c4390103f5bf29786', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6RP0Q312AZJ8S1Y6R', 'T-177', '2026-05-25 07:11:22', '2026-05-25 07:25:02', NULL, '59a180929bd8894b53a761b2dda7b4f1', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7AXEWDKD4VQNQCACM', 'T-176', '2026-05-25 07:11:16', '2026-05-25 07:27:57', NULL, '231d303368c603a6e01a6da02802fbc4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4VNVEAEYBX3S9Z8Q8', 'T-178', '2026-05-25 08:10:10', '2026-05-25 08:21:46', NULL, '3f7aae66c9e487bef74fd4d93b304d12', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4DTGYPAB29MRDN000', 'T-179', '2026-05-25 08:10:18', '2026-05-25 08:26:21', NULL, '826c15b06fe0aaaf660e31042fe371bd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48P8T23ZPR0BRK6PR', 'T-169', '2026-05-24 16:26:49', '2026-05-25 12:12:29', NULL, '99d40776974ced8e57efaa135df3ea9f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM654XV2TG4K0RVV1CW', 'T-170', '2026-05-24 16:27:01', '2026-05-25 19:38:16', NULL, 'd4a19feb40528dd6d22186c442c46890', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4R8YY69YAARCVE4AC', 'T-160', '2026-05-24 09:13:42', '2026-05-30 11:05:56', NULL, '87a62aa99c4c3e3be11c031c5667ff68', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7G7MK8MP3BGQC36HG', 'T-167', '2026-05-24 16:26:30', '2026-05-30 11:05:56', NULL, 'a7343c529fc0c766f4d7c2a9530614fe', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM78W48QX3JZP0QYAVW', 'T-168', '2026-05-24 16:26:39', '2026-05-30 11:44:28', NULL, '65bd7ae87632b0d65777a26af0bb31f2', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6FMAHXM1XAHHCQC60', 'T-171', '2026-05-24 16:27:09', '2026-05-30 21:11:26', NULL, '556df973d835841f7e8ae0c8cd9dd310', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7ZB63YYNFXM9DQNRR', 'T-174', '2026-05-24 17:11:17', '2026-05-30 21:11:26', NULL, 'db290b95db28db6b5a90d880d8a36fdb', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM71N3YMS82BAYD3M9R', 'T-172', '2026-05-24 16:27:16', '2026-05-30 21:34:56', NULL, '8fca1700a4bf028e071cf30e845ee8f1', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6V6TKRPA106D5SMMM', 'T-180', '2026-05-25 09:15:58', '2026-05-31 08:49:07', NULL, '412af567b67756da2e6b438c6a4bfd45', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7WE0SD9N4HCP9BB64', 'T-183', '2026-05-25 09:26:40', '2026-05-31 09:30:11', NULL, 'f09d4e58d36e3049bbcf5d92df301530', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM55DZN2RFEVP6CG48G', 'T-184', '2026-05-30 11:42:53', '2026-05-31 10:17:09', NULL, '7f2e958614fa540d1087d3f1c84568df', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM52PRNJM86A1DZTQ1C', 'T-185', '2026-05-30 21:34:26', '2026-05-31 10:22:02', NULL, 'd7d14700bc8fb0164e6e2852b7a358cd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5HM3N07VB3RPFJCFM', 'T-164', '2026-05-24 16:26:05', '2026-05-31 10:22:03', NULL, '8bc4ad2dbf63176d9674cd014c6cce7b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4TDSA2PDCV814E3YW', 'T-95', '2026-05-08 10:48:59', '2026-05-31 11:20:21', NULL, '628386f31fcfb4e96eac7b503ab2eb4b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5316HVGDYD2W0DC50', 'T-162', '2026-05-24 09:29:07', '2026-05-31 14:17:22', NULL, '46d071266f94b404760832f3c2d88927', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM514YJBF8AY8Q4N7Q4', 'T-188', '2026-05-31 11:38:28', '2026-05-31 14:17:22', NULL, '7328143d32f30fc412c22b5d76de92a9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4GVWHMRTMQYRTRKD4', 'T-187', '2026-05-31 11:38:20', '2026-05-31 14:17:22', NULL, 'ce42be5c4b71e4c155b26e22506045f4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4DAQ2CEFESG9JGMZ8', 'T-192', '2026-05-31 12:08:42', '2026-05-31 14:46:39', NULL, '6bd69db546e3d3aadb77b5c289db817a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6S7FDAXQX32JNCSFG', 'T-186', '2026-05-31 11:37:34', '2026-05-31 16:40:19', NULL, 'eed0473280a7c582289f0fcaa6bf6dd5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM709YPWECH1E66XXD4', 'T-193', '2026-05-31 16:12:42', '2026-05-31 17:26:25', NULL, 'ffbdde83a1cd6bbe9046f686705b2e15', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6YYG4X5R1GX97TJB4', 'T-52', '2026-04-23 20:32:06', '2026-05-31 18:54:02', NULL, '1e34fa00e15b0ada663e117c7493ca69', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QPGVP54EKJPCNQQM', 'T-51', '2026-04-23 20:32:06', '2026-05-31 18:54:02', NULL, 'cc6cf69d394f87a29c685b27ec13155e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM452XPSG99PA60ZZ48', 'T-53', '2026-04-23 20:32:06', '2026-05-31 19:41:13', NULL, '22e98aa4226d3e9979abd7b7cdf7b68a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
@@ -0,0 +1,180 @@
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7MFNNCC0MMY2TCNRM', 'T-194', '2026-06-01 06:49:25', '2026-06-01 06:49:47', NULL, 'c4ed6eb84383fb6052a61f94e37c98c9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4M2Z9YZ0SA81E5Q08', 'T-195', '2026-06-01 06:56:05', '2026-06-01 07:08:16', NULL, '6d199187494f2f942598092f9743285f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6NTYAB8K6T50F8W00', 'T-196', '2026-06-01 08:58:10', '2026-06-01 10:45:14', NULL, '57fadff50bee964e4d821507d0e6e79b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6HQXNNF05GXWK7BWM', 'T-198', '2026-06-01 11:11:32', '2026-06-01 11:31:39', NULL, 'f203d6089bcb5f23d81e4c3cf67b3d33', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM68APBCQTMGJAZ2B9C', 'T-199', '2026-06-01 11:38:32', '2026-06-01 12:35:04', NULL, '6fda1ce6b9f1ea55f001f1840fef1f7a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4RAKSX59W1HJCZ1M8', 'T-200', '2026-06-01 14:04:00', '2026-06-01 14:04:24', NULL, 'b2c666f841d2546d0e6f150dec9e901b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM57GY6PTG78SD3961M', 'T-201', '2026-06-01 14:25:59', '2026-06-01 14:45:39', NULL, '289f9f811f27bad8b3f01acda2651b4b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7G8QBJ2PNQ0VREN24', 'T-197', '2026-06-01 08:58:19', '2026-06-01 16:36:38', NULL, 'a29f2ef4fa8bc86eceff40c324d613d0', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5A3984KFPD4NKRG6M', 'T-202', '2026-06-01 16:48:31', '2026-06-01 16:48:56', NULL, '726f989a61da913e9f41f3353e453541', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM643F7Z648KNFPT7GR', 'T-203', '2026-06-01 16:48:31', '2026-06-01 16:48:56', NULL, '736af2dbfdf6cfbc8d2683c8e0feb063', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM65R8CNFNE1MA5GGR0', 'T-204', '2026-06-01 18:48:19', '2026-06-01 18:51:08', NULL, '7685b5bd09a25bb7094a7458f292e81b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6M83TTXHRXESFZY14', 'T-207', '2026-06-01 18:48:47', '2026-06-01 18:59:19', NULL, '0fd6c0e9537080a0b8ab85f351161542', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4PH1J6ENYT4PJZRHG', 'T-205', '2026-06-01 18:48:27', '2026-06-01 19:12:01', NULL, 'e874c6bf6c97dae25404d6f1044f9c53', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5REFZXMY8ESWN0Z90', 'T-206', '2026-06-01 18:48:40', '2026-06-01 19:27:18', NULL, 'cdf5b34234372c3de0ef0c3caecabed2', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4C2PSN2GVJY9JDSZG', 'T-210', '2026-06-02 18:13:52', '2026-06-03 09:00:37', NULL, '1281485b320f6c146f8af2519c0c02f3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM48QJV4N7J62436GC0', 'T-213', '2026-06-02 18:13:52', '2026-06-03 09:00:37', NULL, 'ac6cf78658e8a9ddfee7573e125377ed', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7171NRB1T82TGP5RG', 'T-211', '2026-06-02 18:13:52', '2026-06-03 09:00:37', NULL, 'bf74e45672e065ef2aa21ce85c341bb0', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM54WTNQDMYT0AVR4WM', 'T-224', '2026-06-02 18:13:52', '2026-06-03 09:05:50', NULL, '16525f2ae7035719b319a6f579d5e316', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4TP6KXTB1GHKBW1G4', 'T-215', '2026-06-02 18:13:52', '2026-06-03 09:18:28', NULL, '4fcb599f4b489ac8050770416737295d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7WYF8ESBSGAQF1S48', 'T-217', '2026-06-02 18:13:52', '2026-06-03 09:18:28', NULL, '682a13f3d6e396600a5d51d38e9508d7', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM51CNEJP52P79RDVJW', 'T-216', '2026-06-02 18:13:52', '2026-06-03 09:18:28', NULL, 'e89cf8d8a6c65f6c47ef73aa0f1a8d7d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM78J2H2S0R79SSSPW8', 'T-214', '2026-06-02 18:13:52', '2026-06-03 09:18:38', NULL, '32ca757e5489e190af0dae4297b200d7', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7XZQJAK31WRVQ0HGW', 'T-228', '2026-06-03 09:49:35', '2026-06-03 10:02:02', NULL, 'ce3d4e957ec8cf656f8f022e518bf8d0', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7SA9TEX5CNMTG67MW', 'T-163', '2026-05-24 09:42:44', '2026-06-03 10:05:55', NULL, 'abed11805979ce7f3d996679ad7007a4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5PFAYE4XH7WV6VDW8', 'T-227', '2026-06-03 09:48:23', '2026-06-03 10:09:26', NULL, '99368eee1c22ea3cf782b52159c04dc9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM68T660K07YH3X5DH0', 'T-229', '2026-06-03 09:51:28', '2026-06-03 10:09:26', NULL, '9d77e30754feb77e0a64a4aa5d47f668', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6Y5SET5WGSS2W78F8', 'T-219', '2026-06-02 18:13:52', '2026-06-03 10:31:10', NULL, 'fae2d0c2950c6f4050c70e7c3c07a525', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6EGSD0SB7NYNSP1BM', 'T-220', '2026-06-02 18:13:52', '2026-06-03 10:59:25', NULL, '3c812d20f5da80c0b936cb59edd2704f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM67ET3VQP90WRXVSMG', 'T-181', '2026-05-25 09:16:05', '2026-06-03 11:41:17', NULL, '045b971616418029b63a850050eef8a5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM758PXMJH16PA3JHFW', 'T-221', '2026-06-02 18:13:52', '2026-06-03 11:50:02', NULL, '9e906306ef56b2c22724d0b72f14ce2d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7AMEYEBTF8YGJ4BVG', 'T-218', '2026-06-02 18:13:52', '2026-06-03 11:50:08', NULL, '9c2a9480a37df7681fe4388e27345573', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6GJRWZVHZF24TPPS4', 'T-226', '2026-06-03 09:37:54', '2026-06-03 11:50:23', NULL, '6bf4765285f29d4c8fce8d71472bae0c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM427BVZYN6MD7ZZX74', 'T-231', '2026-06-03 13:17:32', '2026-06-03 13:30:05', NULL, 'd6eb4f7458f0d1c72432b87a23aa60e3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5BJCK5YG3R0NEEF70', 'T-232', '2026-06-03 13:29:49', '2026-06-03 13:41:42', NULL, 'f255d56c3872bf6a210734e127a4a4bd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JR6QZDMKE167C4MR', 'T-82', '2026-05-06 08:30:15', '2026-06-03 14:56:07', NULL, 'd2669b112125f4f76041885296f5d315', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM492DTRZJK9CGQPGDC', 'T-234', '2026-06-03 13:49:44', '2026-06-03 15:04:07', NULL, '92a7cabc855b76f429217cc65fcfa308', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5GH6VP8TZA95NMYK0', 'T-230', '2026-06-03 11:15:39', '2026-06-03 15:17:33', NULL, 'eec8da81c7180e8f301196514ba54e3b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM46ANXPW02XGD3D3NG', 'T-237', '2026-06-03 15:43:45', '2026-06-03 21:29:47', NULL, 'f9b1b279c9292d892ac07b7f4ea7cef8', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4W624VD4JW1GHDHAR', 'T-182', '2026-05-25 09:26:31', '2026-06-04 12:00:09', NULL, '35ccb60111678fbc43644460a31f1d61', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM56FKPV4WSPWA0KMB4', 'T-189', '2026-05-31 11:38:36', '2026-06-04 12:00:09', NULL, '3759a2e27d13bddbcc19ed9a4fffef9a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5V7GHH42RHV04DPTR', 'T-65', '2026-04-24 06:34:16', '2026-06-04 12:00:09', NULL, '544a078f04d5c926a47cbfdacb4e3e2f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5BWXW61EVYGMZD47G', 'T-191', '2026-05-31 11:58:19', '2026-06-04 12:00:09', NULL, 'd186513cc0783306b2c4e1b2e789dff7', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5ABRPK2SPW8VY7RP4', 'T-190', '2026-05-31 11:38:42', '2026-06-04 12:00:09', NULL, 'fac1c8383ac95413e8885f8ee64b0d54', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM41S6RRTMTY9TYZA50', 'T-122', '2026-05-18 09:06:59', '2026-06-05 13:08:05', NULL, '600101f09173e61159a24ed44e34dd71', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM66TK8TQ1X5T9GK6V0', 'T-42', '2026-04-22 21:03:40', '2026-06-05 15:17:09', NULL, '223a9820685b3da2a6a6e19cf6ad036b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5WT3BHB8JZY77G5AW', 'T-243', '2026-06-05 15:14:25', '2026-06-05 15:17:09', NULL, 'd82988ff85cd644376a13f49e406138f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KPN4VK4EZP013NNG', 'T-244', '2026-06-05 15:21:10', '2026-06-05 15:24:35', NULL, 'f375c96bfd96b9cdd89db0cd046318f3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5YYY4FVPX9KRVHCMC', 'T-50', '2026-04-23 20:31:48', '2026-06-05 21:12:23', NULL, '581605b67cbab29fdcddbac46154308a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM66DNEK5FS9C0T4J5R', 'T-245', '2026-06-05 21:20:00', '2026-06-05 21:24:52', NULL, '577902ab47810d0f848f48c8bf9b3196', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6DH7C6GQN9WNKQNXR', 'T-246', '2026-06-05 21:28:44', '2026-06-05 21:29:50', NULL, '4ce06c5b0b8a09d452934ae8ba1e5c0b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6QCYT7JBF352YVJ48', 'T-247', '2026-06-05 21:28:48', '2026-06-05 21:30:04', NULL, 'c2fbcdc7eda67938f8d35f64a92782b4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5F4D2Q363QF7V40K8', 'T-239', '2026-06-03 21:41:58', '2026-06-06 06:44:40', NULL, '7312cd1c56d3c8d60625df7712a7deb4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5F2GC0ABFFVAH34EC', 'T-238', '2026-06-03 21:11:40', '2026-06-06 07:24:00', NULL, 'a724281049b28271d21c87776313dd72', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7ZQNDVPAPFSD9KYS4', 'T-251', '2026-06-06 07:40:43', '2026-06-06 07:40:43', NULL, '9f010efd2f1b44840ff0af4d3723a4af', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4BVE5EYM92CWGSXA4', 'T-250', '2026-06-06 07:26:50', '2026-06-06 07:49:08', NULL, 'a8c1c3f7d308c4fe005fb998b7ec6730', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7JXD3FKW91934D0W0', 'T-249', '2026-06-06 07:21:17', '2026-06-06 08:21:40', NULL, '9f43580f953cac1a27dab6e0ac2deece', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM70TW38E78FFXDHYAC', 'T-248', '2026-06-06 07:21:17', '2026-06-06 08:31:25', NULL, '577fda356a95641dd39c28a716227d1a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5NEXC0P46NJHACNM0', 'T-233', '2026-06-03 13:29:59', '2026-06-06 09:20:02', NULL, '4cea7def08ae19b77ecfdd04b11f4b5a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM78Q3VYWSE830CTVB4', 'T-212', '2026-06-02 18:13:52', '2026-06-06 10:02:03', NULL, '75bc38afe04d24ff77c2b55d9b764fe1', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QYYKHPQ03H3QD61W', 'T-209', '2026-06-02 18:13:52', '2026-06-06 10:02:06', NULL, '4f782ec2f1fb76eea268247cf4cd75a0', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4K2AAPK52Y8BEJ3YR', 'T-257', '2026-06-06 14:01:03', '2026-06-06 18:58:25', NULL, 'd79886881baf9d88ff31ec2970da2712', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5KYAV8TMH3Y3FPRSR', 'T-23', '2026-04-22 14:08:40', '2026-06-06 19:06:30', NULL, '9b456adde02b184d5d46652aac210af9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM72ACQSDBJTKRBC58W', 'T-223', '2026-06-02 18:13:52', '2026-06-06 19:24:44', NULL, '7909a9620aa2a05d6a02b36f030f3641', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM620F5ENRYNT9S0JDW', 'T-222', '2026-06-02 18:13:52', '2026-06-06 19:24:44', NULL, 'b58b0643ed78d32df526e765a076b2eb', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5DYC7CRP8TMHADW8W', 'T-225', '2026-06-02 18:13:52', '2026-06-06 19:37:47', NULL, '6d2670282a9f66635c2fccaa20162209', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7XMQ44AVCAARQZX14', 'T-208', '2026-06-02 18:13:52', '2026-06-06 19:53:07', NULL, '015f7fe6ec6e3b348a6157401742472b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6S7KHYPE9W7S1JHZ8', 'T-256', '2026-06-06 10:07:35', '2026-06-06 19:53:07', NULL, '2dbd4405f72911a87fd757c819b9f0c5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6WZ7DKVDFAGQ4QDMG', 'T-258', '2026-06-06 20:55:49', '2026-06-06 20:55:49', NULL, '4bcfdcf889c4bf6ca3405fa7b4857520', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7RQMN1GKEZ5JQKJT0', 'T-259', '2026-06-06 21:12:44', '2026-06-06 21:12:44', NULL, '9efbdb558a1358b67827a0814fa5c23e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5JK0RHMRAH47K8ND4', 'T-36', '2026-04-22 20:34:09', '2026-06-06 21:14:22', NULL, 'bab3e0865388263d61db6651c3e5a55d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6DSPH569T4458B8JC', 'T-260', '2026-06-06 21:46:42', '2026-06-06 21:46:42', NULL, 'e45747cbef04e469b21ef0b4f2a3122f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7GJW4V3CXMMXXV988', 'T-261', '2026-06-07 08:03:10', '2026-06-07 08:03:10', NULL, '54c9a91b6b4408f05c85ab0afb7daf47', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7GCVGCH9RMGYHENCC', 'T-54', '2026-04-23 20:32:06', '2026-06-07 08:03:17', NULL, 'ed036c636484f9b724fe82dace896d49', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4QW8VH454J8V3E174', 'T-252', '2026-06-06 08:28:43', '2026-06-07 08:32:36', NULL, 'cb647a08e949e13f6386fefbb99c8c6e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM63WP564EZRERJHVCM', 'T-255', '2026-06-06 09:56:23', '2026-06-07 08:51:08', NULL, 'e2aa43fb1e6a1c9c28ebd1fe2ab6bd79', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM74JAV0MJMB4VQNTRG', 'T-268', '2026-06-07 09:35:03', '2026-06-07 09:36:59', NULL, '796bd5eff55061d92fe00b3c4b15d3ad', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6P9YQ5V7WS6RFJNHW', 'T-269', '2026-06-07 09:44:17', '2026-06-07 09:51:32', NULL, '6fc792db7c0d2ba8f8c73c1e1960faf6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4XBRZJ6DEHFWREWC0', 'T-271', '2026-06-07 10:42:32', '2026-06-07 10:42:32', NULL, 'a7a8244eab996f86026f60edd5969de6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM67PDXAFTPPW7B1WDR', 'T-272', '2026-06-07 10:42:38', '2026-06-07 10:42:38', NULL, 'd3fcdcc97810a9b5f6b0624a3507c952', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM40JE9MZZWCXC91THG', 'T-270', '2026-06-07 10:23:13', '2026-06-07 10:43:18', NULL, '014c8442d8f19f6cc075ad8fc27ce9ed', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM551VZ2NYV6PD0XQRC', 'T-48', '2026-04-23 20:30:09', '2026-06-07 15:40:06', NULL, '8389d1a6a5d2db863bda0f7405d9496a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM71P14PAF7K4QE61G0', 'T-262', '2026-06-07 08:10:42', '2026-06-07 17:41:11', NULL, '561c4e90d842e6ac1a22dcacd7a5e33f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5W6VN98RQM6S22X28', 'T-274', '2026-06-08 07:26:13', '2026-06-08 07:46:46', NULL, 'd9177bbb472fd16a13d4f23e0cd70344', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5TWC00GW0P3X02HZW', 'T-276', '2026-06-08 07:46:23', '2026-06-08 07:53:32', NULL, '1b77c5f3a8d66247532ead2164b6b37f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM60PAS5RJ55CZGPM1G', 'T-277', '2026-06-08 08:32:30', '2026-06-08 08:34:47', NULL, '70e24eabce743397720db9d6029a3337', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7XXSERDHKR0EEH3N4', 'T-263', '2026-06-07 08:34:02', '2026-06-08 08:45:23', NULL, '1cb588e500a902d2b880f3004c5c33b4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM45HHRBCRMV2166Y6M', 'T-266', '2026-06-07 08:41:13', '2026-06-08 09:04:12', NULL, '9162b1edab8984a25660f0fdcebcd41d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5YYMMKFD5Z8WG9F60', 'T-264', '2026-06-07 08:40:25', '2026-06-08 09:25:50', NULL, '4b9c66ff7114ce85456f57f6d1c58d7b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4Q1Q97CHA0XNK2G4W', 'T-265', '2026-06-07 08:40:33', '2026-06-08 10:09:14', NULL, 'ff24fcb74089501588a4bfcfa62c9aad', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7ZGZ9GQ5W1MKV00Y4', 'T-267', '2026-06-07 08:42:53', '2026-06-08 10:09:21', NULL, 'd860955c7a7030d4c1c2ea98e5447fdf', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM55CV3VDYS88VBJZ60', 'T-273', '2026-06-07 17:39:10', '2026-06-08 11:44:27', NULL, '02cd1eaf484bd3adef09a71a7408674e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM59DG2WADJ9A0JFD1W', 'T-281', '2026-06-08 11:46:11', '2026-06-08 11:49:36', NULL, '1d9df18227de229f12bda189c4081353', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6K1QRC911JMQ9C960', 'T-283', '2026-06-08 13:39:51', '2026-06-08 13:44:57', NULL, '56929fab7dc064046853959720d43b8b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6A6A56VFSVRRBKMP0', 'T-279', '2026-06-08 10:21:20', '2026-06-08 14:12:46', NULL, '1023e2106149f0366bf3896635ec44c4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KFHAS1WZFGBJ6JN4', 'T-284', '2026-06-08 14:55:43', '2026-06-08 14:57:15', NULL, '75d5f69fd00e63d09e707ad1e8338853', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6GZ55VD4SAZZ6XXP4', 'T-285', '2026-06-08 15:22:32', '2026-06-08 15:37:29', NULL, '994bf6438e27af887c2f3d247e76d576', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7W2MWZ45YNK7TJGC4', 'T-282', '2026-06-08 13:39:15', '2026-06-08 15:42:01', NULL, '9d5f153703ebeae7f011f555f380ef6e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5AAA9WW54JGP83HGW', 'T-275', '2026-06-08 07:28:34', '2026-06-08 16:52:13', NULL, '886bd74d7ec01145f8acb5c7d9397307', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6NNRR61E8P6GHXABG', 'T-289', '2026-06-08 20:16:22', '2026-06-08 20:16:22', NULL, '6c2022166a2139a665912c3fe6d4802e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4501EBPDFDH84RJ5G', 'T-288', '2026-06-08 17:52:51', '2026-06-09 06:19:01', NULL, '16b1c85fab4273ebe7c9cfb9b9800e3e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5R93N5J6RNVTBRB28', 'T-286', '2026-06-08 16:30:27', '2026-06-09 06:19:36', NULL, '22c1b0f40e3e3344f0c624af618bdaff', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM44CKN5QQGFKP1KWZC', 'T-290', '2026-06-09 07:15:14', '2026-06-09 07:15:14', NULL, '53ca5591666fa7c01d90174bf28ecf4b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM45BSF1B0R0JA1Z7FC', 'T-291', '2026-06-09 07:15:22', '2026-06-09 07:15:22', NULL, '79e9f769c9313f0bc63d5c8c8a1ee641', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5E41Q8TX8X55X4MZ0', 'T-29', '2026-04-22 20:17:26', '2026-06-09 10:46:41', NULL, '93ab2ce409ff22e37a756e41553c0a69', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM50BPNDT50PM24ZVA4', 'T-278', '2026-06-08 09:20:00', '2026-06-09 15:03:23', NULL, '0c340b4aeb41ddaa858ae18888207b2e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4BD8PXDRHBF001200', 'T-292', '2026-06-09 14:57:32', '2026-06-09 15:09:05', NULL, 'b907503e714ddc775257c90330c25d2f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5DM43Q9HX352STN3M', 'T-295', '2026-06-09 15:26:00', '2026-06-09 15:26:00', NULL, 'c93802bd37eb8dfe981c74cfde3f24c4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM42WW0SY3XEXKE8PK8', 'T-293', '2026-06-09 15:12:15', '2026-06-09 15:36:15', NULL, '55da76c0ebfc56f44cb4096a2a17b9c6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4GN07FN8GAXZ20YAR', 'T-236', '2026-06-03 15:31:01', '2026-06-09 15:55:06', NULL, '18d3d9bc2bc268d007d27c4cc20fcea8', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM61SRVBDRFFN2S80T4', 'T-254', '2026-06-06 09:32:54', '2026-06-09 15:55:06', NULL, 'ae31cf118f94e93699eed5f0ac0eee7c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM63TRGBFRWK9W2WA98', 'T-296', '2026-06-09 15:43:58', '2026-06-09 16:34:46', NULL, '405de86b123fc1aeccf6f444a36bbf36', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM72SHY93S36VM9G6D8', 'T-294', '2026-06-09 15:18:22', '2026-06-09 16:34:46', NULL, 'baa483c978c2744aaf95035de3809048', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6RXN6HW6XTEHKYQDG', 'T-297', '2026-06-09 16:01:09', '2026-06-09 16:34:46', NULL, 'f228757d5b5ed41b7cf90b6035e09dda', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7K1FN7KD8S5ZVX2VM', 'T-299', '2026-06-09 16:42:47', '2026-06-09 16:42:47', NULL, 'a2fdfc4298353c34568d42e71c2786e6', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM53HQD6YV754GKBA60', 'T-44', '2026-04-23 20:25:26', '2026-06-09 16:56:55', NULL, '75a46ae8c87aa5a9a27e513b4ea65e58', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM62FKQQD0B9B80PFY4', 'T-158', '2026-05-23 20:48:38', '2026-06-09 17:16:34', NULL, '1282d8b5cb009f639f7c7a14e69ef2b3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7D5DMYF5X92Q55CBW', 'T-235', '2026-06-03 15:17:29', '2026-06-09 17:16:34', NULL, '1bccb4cf47ba2a1a72e4f65a214e2f78', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7W0NPAEG3KWNFY9DR', 'T-132', '2026-05-22 15:45:15', '2026-06-09 17:23:25', NULL, 'cf1709ddff1d13a7b4adb0dc193b319b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7F2TBJJV1F2KP7XR8', 'T-300', '2026-06-09 20:02:41', '2026-06-09 20:03:08', NULL, '6f1c204fc4f81403ba50e9e7ecff7241', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6048VEGFDNEBHFMM4', 'T-301', '2026-06-09 20:04:29', '2026-06-09 20:04:54', NULL, '89490f3e4d3e09714b70f0e35c726038', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4AE2GEF6BXSGKRMGC', 'T-242', '2026-06-05 10:17:47', '2026-06-09 20:07:30', NULL, 'c570cc4f2808f31192d947affb3392a0', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM42VPM50VKAWXBCAM8', 'T-253', '2026-06-06 09:32:53', '2026-06-09 20:17:04', NULL, '47b9d1a167b65553a14ede7a5a53f851', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM7H8JWCP59WQVD0FW4', 'T-302', '2026-06-09 20:24:54', '2026-06-09 20:25:49', NULL, '8e04d93f6163a8f54965bc5988dae31c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5QJC039TMDF5JGMXC', 'T-240', '2026-06-05 09:38:46', '2026-06-09 20:27:17', NULL, '4879c042965effced12e608af5739659', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM531VE0C6N36AT12SR', 'T-241', '2026-06-05 10:02:31', '2026-06-09 20:34:30', NULL, 'b7de744d4e5292306b1fa64bcc3ba1aa', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4K1MTP2MHRKKNXBFM', 'T-303', '2026-06-09 20:41:22', '2026-06-09 20:41:22', NULL, '1adc6db2fcc64d6fd36ad0406939a8d7', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4BRWWPB4R06Q31XV0', 'T-56', '2026-04-23 20:32:06', '2026-06-09 20:57:53', NULL, '5a394b49d98e119d21b637745798fd8b', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM6SK3D1QYACBPHHBZ0', 'T-287', '2026-06-08 17:12:49', '2026-06-09 21:21:08', NULL, '7d9537b53ade941ecd8a8efadd591c17', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KS233FGZE9H7ABWR', 'T-304', '2026-06-09 21:22:28', '2026-06-09 21:22:44', NULL, '252c572a5954883d0e5e71406b092a7f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM50S6WRD81V3Z0NH1M', 'T-298', '2026-06-09 16:06:41', '2026-06-09 21:36:05', NULL, 'f8666523eaf2642b918f6846e2801539', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5YNEQM1WY25BV7TD8', 'T-280', '2026-06-08 11:29:22', '2026-06-09 21:42:59', NULL, 'e9859dd425e0ac667ddbce721415572e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB16FJ7KXGFHQXEG88MYRFTG', 'T-305', '2026-06-10 08:15:39', '2026-06-10 08:15:39', NULL, '288b0c9d74e98c2ac42ee25d3369f689', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB16YM1Y910T07Y1Y22MGTEM', 'T-306', '2026-06-10 08:17:42', '2026-06-10 08:17:42', NULL, 'af36af77224b697a61cb5447955605e5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB170DJA02EH2E8HMH7X7SS4', 'T-307', '2026-06-10 08:17:57', '2026-06-10 08:17:57', NULL, '882cca1753c2ede7bfe2aef142545b76', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB1Q1Y3CYJHD7W5F68VDB6T4', 'T-308', '2026-06-10 09:28:04', '2026-06-10 09:28:04', NULL, '5050ff0e69b24a93f5b7e96c8a9fa47e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB1S7613SYF0M9XQT5JNWM40', 'T-309', '2026-06-10 09:37:31', '2026-06-10 09:37:31', NULL, 'b4f48310ea6b0f49f83c46e3bc5b7ad8', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB1SJKA7KRCQMZE41SPQG780', 'T-310', '2026-06-10 09:39:05', '2026-06-10 09:39:05', NULL, '9088122bf9d5e04ca8a7fee3bf031a53', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB1XDWKQ594ET4GDYEFK5ZJ4', 'T-311', '2026-06-10 09:55:55', '2026-06-10 09:55:55', NULL, 'ed03464125d09ff5f23913e6247064e3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB1ZFJK6J2GSV4SA69QF730C', 'T-312', '2026-06-10 10:04:53', '2026-06-10 10:04:53', NULL, 'dd22af12b333c13376b23aa5381ea2d7', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB234WP4Y6Q16A0HFW8BSXMG', 'T-313', '2026-06-10 10:20:54', '2026-06-10 10:20:54', NULL, '9b7abb73ea56cfdad2039303a8935897', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB23Z52EVGQ4ZDAQ5BVAJ13R', 'T-314', '2026-06-10 10:24:29', '2026-06-10 10:24:29', NULL, 'f8b89421a13f620c2a0e76bd780db5a2', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ACSDBDZARV3NNGYD9NYYR', 'T-315', '2026-06-10 10:52:34', '2026-06-10 10:52:34', NULL, 'd0521ab80265d66c99c803a8714e8f02', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2AD3HPR3HXSVASVZEX8PK0', 'T-316', '2026-06-10 10:52:36', '2026-06-10 10:52:36', NULL, '5beb005b263eb4c5bb10ffc6e2fc0100', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2EDCBYRBDSV9V1PJ1KE3CM', 'T-317', '2026-06-10 11:10:07', '2026-06-10 11:10:07', NULL, '4d4a02ab95987e3ebb134a2f0173b4a1', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', 'T-318', '2026-06-10 11:11:40', '2026-06-10 11:11:40', NULL, '31820aadd1f19f9dd0cf931dae565abc', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ESCR4V6V07CRH18FBCDN8', 'T-319', '2026-06-10 11:11:45', '2026-06-10 11:11:45', NULL, '12c5a57fb83444410cca98a802e9ba6f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ETJQP0CT6X7W3CWZ6NS9G', 'T-320', '2026-06-10 11:11:55', '2026-06-10 11:11:55', NULL, '2266e362f144e4fd97403e1d1abe3eb4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2EV29HSK6EJ5VF50R87VC4', 'T-321', '2026-06-10 11:11:59', '2026-06-10 11:11:59', NULL, 'd48b6bd9f0c8289c95448dda6865d666', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2G1WD1839Z90AQ5C0BHNV4', 'T-322', '2026-06-10 11:17:17', '2026-06-10 11:17:17', NULL, 'f33b1db4b67a0521f02c5c49fcfa8e6f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2G2KHKT5CJYR0TK1WQGMD0', 'T-323', '2026-06-10 11:17:23', '2026-06-10 11:17:23', NULL, '0738a8da475a6d8d49132d1e4e753775', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2J2HWD66QAFDDRRWS5NM48', 'T-324', '2026-06-10 11:26:07', '2026-06-10 11:26:07', NULL, 'd8aa9ae4522d97b70331c42b8ab7a365', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2T11GCV1EV07DYD5BZENTM', 'T-325', '2026-06-10 12:00:52', '2026-06-10 12:00:52', NULL, '47b8f31d9337c5bbc6476d62c7f6ed4d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2TY91VHK7TPKPMZ11EG3TM', 'T-326', '2026-06-10 12:04:51', '2026-06-10 12:04:51', NULL, '348dee79d9d3fe0a8b260c1f9c47a289', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2W4G9K8ZF782W7H2TM5XA8', 'T-327', '2026-06-10 12:10:04', '2026-06-10 12:10:04', NULL, '967aab6bc692754a5d3a192cb0008871', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB37JZSFZKWPK9PDFYJY2YC0', 'T-328', '2026-06-10 13:00:06', '2026-06-10 13:00:06', NULL, '553b6683bbcddfbbab39ee545a99c98c', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DHCTP001YCHFP39XER0ZM', 'T-329', '2026-06-10 13:26:06', '2026-06-10 13:26:06', NULL, 'a7b63cf73534de82c953d3029251b5e4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DJZDDZ00BSA04B660RS7M', 'T-330', '2026-06-10 13:26:19', '2026-06-10 13:26:19', NULL, 'c7ef552dc6d7fc2a5e312b20939a2987', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DKQQJ583944DG8561VQ3G', 'T-331', '2026-06-10 13:26:25', '2026-06-10 13:26:25', NULL, '002e4d81284119aecab05d180a9a41cd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DMF20SYFDT6WX2RFBQXKW', 'T-332', '2026-06-10 13:26:31', '2026-06-10 13:26:31', NULL, '38d661bbd4a937daf395f82074cf8d12', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DN94MBCTYJW17ZCYVSXE0', 'T-333', '2026-06-10 13:26:38', '2026-06-10 13:26:38', NULL, 'c710c528662f92c6b7416b7163c8e99f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DNQZKV20F7YG5PJH8V8SM', 'T-334', '2026-06-10 13:26:42', '2026-06-10 13:26:42', NULL, '7f774dc51ae344eddfd42c2149887d56', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DP48FS33CQGRDF7EB9GT0', 'T-335', '2026-06-10 13:26:45', '2026-06-10 13:26:45', NULL, '1cce32ce77997202bf74c69426c29ab9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DQEMTDHF8SV27AKAB8JHW', 'T-336', '2026-06-10 13:26:56', '2026-06-10 13:26:56', NULL, 'b988abcd2cef0dbd6adbc56502f676ca', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3DWCJSGZH9WYDNFWZBAYYR', 'T-337', '2026-06-10 13:27:36', '2026-06-10 13:27:36', NULL, '94b0ebd244a6794e0e4e5a868b38d67f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3JAXDKZMS0805MMEYB6820', 'T-338', '2026-06-10 13:47:04', '2026-06-10 13:47:04', NULL, 'be9b536d471a6008cc854092dfa6aaa4', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3JM0AXK1CTWD720RV1AVZ0', 'T-339', '2026-06-10 13:48:18', '2026-06-10 13:48:18', NULL, 'e76a2ce5d3149bb7ca0ec8a45f5c57a2', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB3KS499THAD899M0NWN7E3R', 'T-340', '2026-06-10 13:53:23', '2026-06-10 13:53:23', NULL, 'bdd164a73576f6ca5151c8a62640526f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB44SKPKTHFMV6WD28GZYPXM', 'T-341', '2026-06-10 15:07:43', '2026-06-10 15:07:43', NULL, '113520f8f3a640fa2f470200ea9f45d8', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB493JEW32CH0H3771TNHF7G', 'T-342', '2026-06-10 15:26:33', '2026-06-10 15:26:33', NULL, '13a646741953f113356c4bcda162c3c9', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4FDREHRYRR7B9ER72KCQKC', 'T-343', '2026-06-10 15:54:09', '2026-06-10 15:54:09', NULL, 'e48d2b913bb03202409c6568d9bc11a5', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4J5E6W983P1S7BE0FDPSMM', 'T-344', '2026-06-10 16:06:08', '2026-06-10 16:06:08', NULL, '481e82dfab98c20cd37c77a3e0c16668', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4RD1DDSYM4J7WYEGTXARB4', 'T-345', '2026-06-10 16:33:23', '2026-06-10 16:33:23', NULL, '9f26d5036c05057d4bd0fc53ddaddafd', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4VG3N3YJSV8G7M1HFSYW2W', 'T-346', '2026-06-10 16:46:54', '2026-06-10 16:46:54', NULL, 'a64940cb0b4ec4fb489eac27065dc447', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB4XCM5KBXDDSCWJ37GPYG3R', 'T-347', '2026-06-10 16:55:10', '2026-06-10 16:55:10', NULL, '9d40226dbc6136072d2d5d0eda71f141', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB50YE6S6YWNP2ZSFWES9B2W', 'T-348', '2026-06-10 17:10:42', '2026-06-10 17:10:42', NULL, '8bb5ad92551f272417840869a0774668', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB58X0TFJ02YTMVPD0D9Q838', 'T-349', '2026-06-10 17:45:28', '2026-06-10 17:45:28', NULL, '0b12f14fa83254ab113c870531628359', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB5CW7JPT6BR2RWMNYVCXJ50', 'T-350', '2026-06-10 18:02:50', '2026-06-10 18:02:50', NULL, 'a5c0c22d84621b14a5208317414d6026', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB5HMYDXP62RKH3HP55T6AYG', 'T-351', '2026-06-10 18:23:41', '2026-06-10 18:23:41', NULL, '9d2da44c16c5aa38c0a36e4b00ef5f15', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB5M14B76B31654D959XM5AC', 'T-352', '2026-06-10 18:34:05', '2026-06-10 18:34:05', NULL, '3fe3e1d5fb7c0fbd084b45116575ad98', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBAWHM1SQ1686ZJ8JQCFQ1ZW', 'T-353', '2026-06-11 06:50:21', '2026-06-11 06:50:21', NULL, '53374633101d04f94981baaf4f2e0315', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
@@ -1,15 +1,8 @@
|
||||
-- Auto-generated by pql init. CREATE TABLE statements
|
||||
-- Auto-generated by migrate-ids (D-26). CREATE TABLE statements
|
||||
-- for the planning schema; per-table dir keeps the changelog
|
||||
-- self-describing per D-15. CREATE TABLE IF NOT EXISTS is
|
||||
-- idempotent so running schema files from each directory in
|
||||
-- replay order is harmless.
|
||||
--
|
||||
-- Importer parses the markers below to detect schema drift
|
||||
-- between the producing pql version and the local one — a
|
||||
-- bumped canonical_version means projection rules changed
|
||||
-- and replay must refuse rather than silently corrupt state.
|
||||
-- pql:created_by: 1.4.26
|
||||
-- pql:canonical_version: 1
|
||||
-- self-describing per D-15.
|
||||
-- pql:created_by: migrate-ids
|
||||
-- pql:canonical_version: 2
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS decisions (
|
||||
@@ -43,14 +36,23 @@ CREATE TABLE IF NOT EXISTS decision_refs (
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
-- Identity split (D-26): a ticket's stable, collision-proof identity is its
|
||||
-- record_id (a locally-generated ULID, planning.NewRecordID); the friendly
|
||||
-- T-NNN label lives in ticket_idmap and may be reconciled. Every structural
|
||||
-- reference (parent, deps, history, labels) targets record_id, so a label
|
||||
-- clash never corrupts the graph — only ticket_idmap needs a relabel.
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id TEXT PRIMARY KEY,
|
||||
record_id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL CHECK(type IN ('initiative','epic','story','task','bug')),
|
||||
parent_id TEXT REFERENCES tickets(id),
|
||||
parent_record_id TEXT REFERENCES tickets(record_id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'backlog'
|
||||
CHECK(status IN ('backlog','ready','in_progress','review','done','cancelled')),
|
||||
-- No CHECK enumeration: the ticket status vocabulary is per-vault
|
||||
-- configurable (ticket_statuses in .pql/config.yaml). Validation lives
|
||||
-- in Go (planning.StatusSet), so adding/renaming statuses needs no
|
||||
-- schema change. The DEFAULT is a harmless fallback — CreateTicket
|
||||
-- always inserts the configured default explicitly.
|
||||
status TEXT NOT NULL DEFAULT 'backlog',
|
||||
priority TEXT DEFAULT 'medium'
|
||||
CHECK(priority IN ('critical','high','medium','low')),
|
||||
assigned_to TEXT,
|
||||
@@ -63,19 +65,33 @@ CREATE TABLE IF NOT EXISTS tickets (
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
-- ticket_idmap maps a record_id to its current friendly label (T-NNN).
|
||||
-- ticket_id is intentionally NOT globally unique: two uncoordinated clones
|
||||
-- can mint the same label, which surfaces as a duplicate-label collision
|
||||
-- (detected at replay) and is fixed with "pql ticket relabel".
|
||||
CREATE TABLE IF NOT EXISTS ticket_idmap (
|
||||
record_id TEXT PRIMARY KEY REFERENCES tickets(record_id),
|
||||
ticket_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_deps (
|
||||
blocker_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
blocked_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
blocker_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
blocked_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (blocker_id, blocked_id)
|
||||
PRIMARY KEY (blocker_record_id, blocked_record_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
field TEXT NOT NULL,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
@@ -89,14 +105,14 @@ CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_labels (
|
||||
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
label TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (ticket_id, label)
|
||||
PRIMARY KEY (ticket_record_id, label)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS meta (
|
||||
@@ -109,6 +125,8 @@ CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_team ON tickets(team);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_decision_ref ON tickets(decision_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_parent ON tickets(parent_record_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_idmap_label ON ticket_idmap(ticket_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_domain ON decisions(domain);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_type ON decisions(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_decision_refs_target ON decision_refs(target_id);
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
-- Auto-generated by pql init. CREATE TABLE statements
|
||||
-- Auto-generated by migrate-ids (D-26). CREATE TABLE statements
|
||||
-- for the planning schema; per-table dir keeps the changelog
|
||||
-- self-describing per D-15. CREATE TABLE IF NOT EXISTS is
|
||||
-- idempotent so running schema files from each directory in
|
||||
-- replay order is harmless.
|
||||
--
|
||||
-- Importer parses the markers below to detect schema drift
|
||||
-- between the producing pql version and the local one — a
|
||||
-- bumped canonical_version means projection rules changed
|
||||
-- and replay must refuse rather than silently corrupt state.
|
||||
-- pql:created_by: 1.4.26
|
||||
-- pql:canonical_version: 1
|
||||
-- self-describing per D-15.
|
||||
-- pql:created_by: migrate-ids
|
||||
-- pql:canonical_version: 2
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS decisions (
|
||||
@@ -43,14 +36,23 @@ CREATE TABLE IF NOT EXISTS decision_refs (
|
||||
PRIMARY KEY (source_id, target_id, ref_type)
|
||||
);
|
||||
|
||||
-- Identity split (D-26): a ticket's stable, collision-proof identity is its
|
||||
-- record_id (a locally-generated ULID, planning.NewRecordID); the friendly
|
||||
-- T-NNN label lives in ticket_idmap and may be reconciled. Every structural
|
||||
-- reference (parent, deps, history, labels) targets record_id, so a label
|
||||
-- clash never corrupts the graph — only ticket_idmap needs a relabel.
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id TEXT PRIMARY KEY,
|
||||
record_id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL CHECK(type IN ('initiative','epic','story','task','bug')),
|
||||
parent_id TEXT REFERENCES tickets(id),
|
||||
parent_record_id TEXT REFERENCES tickets(record_id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'backlog'
|
||||
CHECK(status IN ('backlog','ready','in_progress','review','done','cancelled')),
|
||||
-- No CHECK enumeration: the ticket status vocabulary is per-vault
|
||||
-- configurable (ticket_statuses in .pql/config.yaml). Validation lives
|
||||
-- in Go (planning.StatusSet), so adding/renaming statuses needs no
|
||||
-- schema change. The DEFAULT is a harmless fallback — CreateTicket
|
||||
-- always inserts the configured default explicitly.
|
||||
status TEXT NOT NULL DEFAULT 'backlog',
|
||||
priority TEXT DEFAULT 'medium'
|
||||
CHECK(priority IN ('critical','high','medium','low')),
|
||||
assigned_to TEXT,
|
||||
@@ -63,19 +65,33 @@ CREATE TABLE IF NOT EXISTS tickets (
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
-- ticket_idmap maps a record_id to its current friendly label (T-NNN).
|
||||
-- ticket_id is intentionally NOT globally unique: two uncoordinated clones
|
||||
-- can mint the same label, which surfaces as a duplicate-label collision
|
||||
-- (detected at replay) and is fixed with "pql ticket relabel".
|
||||
CREATE TABLE IF NOT EXISTS ticket_idmap (
|
||||
record_id TEXT PRIMARY KEY REFERENCES tickets(record_id),
|
||||
ticket_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_deps (
|
||||
blocker_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
blocked_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
blocker_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
blocked_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (blocker_id, blocked_id)
|
||||
PRIMARY KEY (blocker_record_id, blocked_record_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
field TEXT NOT NULL,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
@@ -89,14 +105,14 @@ CREATE TABLE IF NOT EXISTS ticket_history (
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_labels (
|
||||
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
||||
ticket_record_id TEXT NOT NULL REFERENCES tickets(record_id),
|
||||
label TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
deleted_at TEXT,
|
||||
hash TEXT,
|
||||
canonical_version INTEGER,
|
||||
PRIMARY KEY (ticket_id, label)
|
||||
PRIMARY KEY (ticket_record_id, label)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS meta (
|
||||
@@ -109,6 +125,8 @@ CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_team ON tickets(team);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_decision_ref ON tickets(decision_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_parent ON tickets(parent_record_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_idmap_label ON ticket_idmap(ticket_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_domain ON decisions(domain);
|
||||
CREATE INDEX IF NOT EXISTS idx_decisions_type ON decisions(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_decision_refs_target ON decision_refs(target_id);
|
||||
|
||||
@@ -16,16 +16,813 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
## [2.3.3] — 2026-06-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Ticket/decision sidebars load on first open after a desktop launch.** A
|
||||
desktop launch starts in HOME (not a git repo), and the daemon booted its
|
||||
pql/git/files workspace there — so pql ran in HOME, hit a stale
|
||||
`~/.pql/pql.db`, and the sidebars showed "pql … failed" until the project was
|
||||
reopened (a manual refresh worked once the workspace had swapped to the repo).
|
||||
The daemon now boots at the last opened project when the launch directory
|
||||
isn't itself a repo, so pql targets the real workspace from the first request.
|
||||
(T-352)
|
||||
|
||||
### Changed
|
||||
|
||||
- **Minimum toolchain raised to honest values.** `pubspec.yaml` now declares
|
||||
Flutter `>=3.35.0` / Dart `>=3.9.0` (was `3.19.0` / `3.5.0`) — the real
|
||||
minimums our deps already required (`alchemist` needs Flutter 3.32; Dart 3.9
|
||||
ships in Flutter 3.35). The exact build toolchain is pinned in `.fvmrc`
|
||||
(Flutter 3.44.1). Moving to the Dart 3.9 language level reformatted the tree
|
||||
to the new "tall" style and adopted two new lints (`unnecessary_underscores`,
|
||||
`use_null_aware_elements`). (T-353)
|
||||
|
||||
### Security
|
||||
|
||||
- **Dependency audit + refresh.** Reviewed every pinned and transitive
|
||||
dependency against the GitHub Advisory Database / OSV (Pub ecosystem) — no
|
||||
advisory affects any current or candidate version. Refreshed the safe pins:
|
||||
`ffi` 2.1.3→2.2.0, `jovial_svg` 1.1.26→1.1.30 (pulls `jovial_misc` 0.10.0 +
|
||||
`xml` 7.0.1), `mocktail` 1.0.4→1.0.5, and `markdown` 7.2.2→7.3.1 (unblocked by
|
||||
the Dart 3.9 floor below). Deliberately held with reasons in `pubspec.yaml`:
|
||||
`alchemist` (0.13 golden churn), `test` (Flutter-SDK locked). (T-353)
|
||||
- **Supply-chain gate (`make security`).** A new `security` target runs
|
||||
`osv-scanner` over `pubspec.lock` and fails if any resolved dependency has a
|
||||
known advisory — a hard gate on top of `dart pub`'s passive, non-failing
|
||||
advisory print. Intended for the CI PR-merge pipeline (kept out of
|
||||
`push-check` so dev machines don't need the scanner installed); run locally
|
||||
any time with `make security`. (T-353)
|
||||
|
||||
## [2.3.2] — 2026-06-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Ticket and decision sidebars reliably load on first open (real fix).** The
|
||||
2.3.1 re-fetch-on-open helped only when a project is picked *after* the window
|
||||
is up; with sticky-startup the project opens during boot, before the panes
|
||||
mount, so they never saw the event. The underlying cause was a race: the boot
|
||||
IPC-server swap (to the launch CWD) and the project-open swap (to the repo)
|
||||
ran concurrently, and the late-finishing boot swap could clobber the repo
|
||||
bind — leaving the daemon's pql/git/files pointed at the launch directory
|
||||
(HOME) and the sidebars erroring on a stale/global pql.db. Swaps are now
|
||||
serialized so the repo bind always wins. (T-352)
|
||||
|
||||
## [2.3.1] — 2026-06-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Frameless window chrome works on KDE Plasma 6 / KWin 6.** The Wayland
|
||||
server-decoration request fired on `realize`, before GTK created the
|
||||
surface, so it bailed and KWin (which defaults to server-side decorations)
|
||||
kept drawing its own title bar. It now also fires on `map`. (T-351)
|
||||
- **pql sidebar panes no longer stick on a transient startup error.** A
|
||||
too-early or db-busy pql failure (the planning DB still settling, or a
|
||||
SQLite lock under concurrent writes) is now retried a few times before
|
||||
surfacing, instead of leaving the pane on "pql … failed" until a manual
|
||||
refresh. (T-350)
|
||||
- **Ticket and decision sidebars load on first open, not just after a manual
|
||||
refresh.** On a desktop launch the daemon's pql workspace starts as the
|
||||
launch directory, not the repo, so the panes' first fetch ran against the
|
||||
wrong (or a stale-schema) DB and errored. They now re-fetch when the
|
||||
workspace actually opens, by which point the pql workspace is the repo. (T-352)
|
||||
|
||||
## [2.3.0] — 2026-06-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Tools like `pql` resolve when clide is launched from the desktop on Linux.**
|
||||
A desktop launch inherits a minimal PATH without `~/.local/bin`, so the pql
|
||||
pane (and other PATH-resolved tools) failed — the PATH expansion that fixes
|
||||
this previously ran on macOS only. It now also runs on Linux. (T-347)
|
||||
- **Consistent card font sizes in the Claude conversation.** Tool/result cards
|
||||
and the Activity/run collapser cards now share the same header-label (14) and
|
||||
collapsed-summary (13) sizes, so neighbouring cards in the stream no longer
|
||||
render 1–2px apart. (T-344)
|
||||
- **"Deny & simplify" no longer shows a loud red error.** A denial the user
|
||||
deliberately chose (Deny & simplify) folds into a muted, collapsed "denied"
|
||||
card instead of the prominent expanded-red block reserved for genuine tool
|
||||
failures — which still render expanded. Driven by a reusable per-result
|
||||
"quiet error" flag, not by matching the note text. (T-340)
|
||||
- **Sub-agent prompts no longer render as a blue "you" card.** In live
|
||||
(stream-json) sessions the spawning prompt is tagged with `parent_tool_use_id`,
|
||||
not the transcript's `isSidechain`/`parentUuid`, so it slipped past the
|
||||
sidechain fold. The parser now treats that field as a sidechain marker and
|
||||
folds the prompt into its Agent card. (T-338)
|
||||
|
||||
### Added
|
||||
|
||||
- **Per-type filter chips on the tickets panel.** A row of toggle chips
|
||||
(Initiative · Epic · Story · Task · Bug, large→small) below the filter box.
|
||||
Click a chip to toggle that type; double-click to isolate it (chart-legend
|
||||
solo); disabling the last one snaps all back on. ANDed with the text filter.
|
||||
All on by default. (T-343)
|
||||
- **VS Code keybinding preset.** A `vscode` keymap mapping VS Code's default
|
||||
shortcuts (Ctrl+P, Ctrl+Shift+P, Ctrl+B, Ctrl+J, Ctrl+`, zoom, …) to clide.
|
||||
Activate via the "Keymap: VS Code" command or `app.keymap.preset = vscode`. (T-64)
|
||||
- **JetBrains keybinding preset.** A `jetbrains` keymap mapping IntelliJ's
|
||||
defaults (Find Action, Go to File, tool windows, …). Double-Shift "Search
|
||||
Everywhere" isn't expressible by the chord matcher yet (T-341), so Go to File
|
||||
stands in for quick-open. (T-66)
|
||||
- **Picking up a ticket now starts it.** Handing a ticket to a live Claude pane
|
||||
(sidebar pick-up) also moves it to `in_progress` and refreshes the sidebar —
|
||||
but only on acceptance and only from a not-yet-started status, so a pick-up
|
||||
with no live pane is a quiet no-op and a re-pick-up never moves a ticket
|
||||
backwards. (T-339)
|
||||
- **Clickable file references in the Claude conversation.** Workspace file paths
|
||||
mentioned by Claude — bare (`lib/app.dart`), with a line (`lib/app.dart:42`),
|
||||
backticked, or as markdown links — are now clickable and open in the editor,
|
||||
jumping to the line when present. Only paths that actually exist in the repo
|
||||
linkify, so prose like version numbers stays literal. (T-300)
|
||||
- **Hand a ticket to Claude from the sidebar.** Hovering a ticket card reveals a
|
||||
run icon; clicking it hands the full ticket to the active Claude pane as a
|
||||
"pick this up and start" prompt. Routed over the message bus, so the sidebar
|
||||
stays decoupled from the session internals. (T-327)
|
||||
- **Claude's task list is now visible, docked above the composer.** When Claude
|
||||
is tracking a TodoWrite checklist, a compact display-only strip shows it pinned
|
||||
above the input — collapsed to `N tasks · M done` + the current in-progress
|
||||
item, expandable to the full list with per-item status glyphs. Hidden when
|
||||
there are no tasks. (T-308)
|
||||
- **A "Deny & simplify" option on the permission card.** A fourth button
|
||||
(alongside Allow / Allow-and-remember / Deny) denies the action with a
|
||||
preformatted note telling Claude it was too complex and to retry simpler —
|
||||
without writing a memory or changing settings. A typed note is appended;
|
||||
addressable by number key (4, or 3 without remember). (T-311)
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Clicking outside the image in the lightbox now closes it.** Previously only
|
||||
the thin margin dismissed — a click on the dimmed canvas beside a letterboxed
|
||||
image hit the viewer and did nothing. A single tap outside the painted image
|
||||
now closes it (matching Esc / the × button); tapping, dragging, or zooming the
|
||||
image still doesn't. (T-309)
|
||||
- **Run-status indicators no longer crash on rapid flips.** Switching status
|
||||
back and forth within the 200ms cross-fade (e.g. running → success → running
|
||||
across two bound Claude panes) tripped an AnimatedSwitcher duplicate-key
|
||||
assertion and a cascade of follow-on errors. Each glyph now carries a key
|
||||
unique per change, so an exiting and entering glyph never collide. (T-326)
|
||||
- **The activity-card run-status spinner is now legible.** At 12px the spinning
|
||||
logo mark read as a static speck; the run-status indicator on collapsible
|
||||
cards is bumped to a `clideIconHero` (26) so the running state is clear at a
|
||||
glance. The check / cross share the size, so the card doesn't jump on settle.
|
||||
(T-304)
|
||||
|
||||
## [2.2.0] — 2026-06-10
|
||||
|
||||
### Added
|
||||
|
||||
- **`clide://` deep links open files, safely.**
|
||||
`clide://open?path=/repo/file.dart&line=42` opens the file at that line (CI
|
||||
links, error reports), routed through the CLI→IPC path into the running
|
||||
window. As an untrusted external vector it's gated by a default-deny allowlist
|
||||
(navigation only) and a confirmation prompt before any action. Registered on
|
||||
Linux + macOS. (T-56, D-90)
|
||||
- **Number keys pick prompt buttons (CLI muscle memory).** In a permission or
|
||||
AskUserQuestion prompt, `1`/`2`/`3`… select the matching button or option
|
||||
(labels are now numbered), and Enter confirms the primary action. Typing in a
|
||||
note field is unaffected — digits only act while the card itself holds focus.
|
||||
(T-240)
|
||||
- **Links in the Claude conversation are clickable.** An http(s) link (typed or
|
||||
autolinked) now opens in your default browser on click — with a hover
|
||||
underline + pointer — across prose, lists, tables, and headings. Non-http
|
||||
schemes stay inert. (T-253)
|
||||
- **The activity-card fold level is now adjustable and persists.** A
|
||||
`claude.activity.fold-level` command cycles how aggressively meta steps fold
|
||||
(none → tools → thinking → everything); the choice is saved app-wide and the
|
||||
Claude pane and team tiles re-fold live. (T-235)
|
||||
- **Consecutive edits to one file fold into a single card.** A run of same-file
|
||||
edits collapses to one `# edits` holder instead of a stack — every edit
|
||||
reachable on expand; a different file or an interleaving step splits it. The
|
||||
card shows an aggregate live status: a logo-mark spinner while editing,
|
||||
settling to a check or cross. (T-296)
|
||||
- **Collapse toggles in the status bar.** A small caret-line button bookends
|
||||
each end of the bottom status bar — left collapses/expands the sidebar, right
|
||||
the context pane. The chevron points inward to collapse, outward to expand,
|
||||
and fires the existing `sidebar.collapse` / `context.collapse` commands
|
||||
(`Ctrl+Shift+1` / `Ctrl+Shift+3`), so it's the mouse affordance for an
|
||||
already keyboard/CLI-addressable action. (T-294)
|
||||
- **Pasted images render inline in the Claude conversation.** A pasted-image
|
||||
`@<path>` reference shows as a bounded thumbnail instead of the raw path;
|
||||
clicking it (or Enter when focused) opens it in the lightbox. The composer's
|
||||
attachment previews use the same larger thumbnail. A missing file degrades to
|
||||
a placeholder; the sent text is unchanged. (T-236, T-254, D-89)
|
||||
- **The editor honours `.editorconfig`.** Opening a file resolves the
|
||||
workspace rules into a source-agnostic `EditorSettings` (own INI parser +
|
||||
glob matcher, `root`/nearest-wins precedence — no new dependency). The editor
|
||||
indents with Tab/Shift+Tab and draws a `max_line_length` ruler; saving applies
|
||||
`end_of_line`, `trim_trailing_whitespace`, and `insert_final_newline`. Saving
|
||||
the `.editorconfig` re-resolves open buffers live. (T-29)
|
||||
- **Permission-mode control beside the Claude composer.** An icon-only,
|
||||
per-mode-coloured button opens a menu of the safe modes (default ·
|
||||
accept-edits · plan); `bypass` shows disabled. The status-bar mode is now a
|
||||
passive colour-coded indicator — switching lives in the control and
|
||||
`Ctrl/Cmd+M`. (T-275)
|
||||
- **Clickable T/D/Q/R cross-refs in the Claude conversation.** Bare ticket and
|
||||
governance references (`T-281`, `D-77`, `Q-5`, `R-2`) in rendered messages are
|
||||
now links that open the record in its context-pane reader — tickets for `T-`,
|
||||
decisions for `D`/`Q`/`R`. Refs inside code stay literal. (T-279)
|
||||
- A Zed-style **application menu bar** in the hat — **File / View / Help**
|
||||
menus built from custom widgets (no native menu, D-7), populated from the
|
||||
command registry with inline keybindings. Full keyboard nav (`Alt`+mnemonic,
|
||||
arrows, Enter, Esc). Help → About shows version + bundled licenses. `Ctrl+O`
|
||||
and `Ctrl+Shift+N` are now real keybindings. (T-48)
|
||||
- The sidebar/dock **filter boxes are now CLI-addressable** (D-6 parity): `clide
|
||||
ui filter <address> <text>` drives a pane's filter as typing would, and `clide
|
||||
ui filter <address>` reads it back. Addresses are box ids from `clide pane
|
||||
list` (e.g. `decisions.panel`, `files.tree`). Routed through the MessageBus, so
|
||||
a click and the CLI behave identically. (T-270)
|
||||
- Click an inline image card to open it in a full-screen **lightbox** — zoom
|
||||
(scroll/pinch), pan, double-click to reset, `Esc`/backdrop to dismiss — since
|
||||
the cards are often too small to read. `clide image show <path> --fullscreen`
|
||||
opens straight into it. The lightbox is a reusable `ClideLightbox` primitive.
|
||||
(T-252)
|
||||
- A bottom **output dock**: toggle it from a status-bar widget (or `⌘J`/`Ctrl+J`)
|
||||
to see logs (Output) and diagnostics (Problems) as tabs — filterable by
|
||||
source/level/text, auto-scrolling. The status widget doubles as a health
|
||||
badge (green `✓` clean, `⚠`/`✕` counts otherwise) and replaces the old
|
||||
app-status item; Problems moved here from the sidebar. (T-54, D-87)
|
||||
- External MCP clients (Cursor, Windsurf, Copilot, …) can now drive clide: the
|
||||
MCP server exposes the full `mcp__clide__*` tool surface, generated from the
|
||||
command registry that already feeds the CLI + palette (D-86), with a
|
||||
per-command opt-out. The two `/ide` tools remain stubs. (T-225)
|
||||
- `clide events --since <cursor> [--filter X]` reads events after a cursor and
|
||||
returns them plus a next-cursor — the pull-based complement to the
|
||||
`tail --events` stream, made for agent poll loops. Reports `gap: true` when
|
||||
the cursor has aged out of the in-memory ring (D-85). (T-223)
|
||||
- "Install 'clide' command in PATH" command (`clide.installCli`) copies the
|
||||
bundled C client to `~/.local/bin`, VS Code style. On launch clide warns when
|
||||
`clide` is missing from PATH or points at the GUI bundle instead of the CLI
|
||||
client. (T-212)
|
||||
- `clide ui open diff <path>` reveals the diff in a split above the Claude
|
||||
conversation, scrolls to that file and highlights its header — the
|
||||
diff-panel arm of `ui open`. Workspace tabs other than Claude/editor now
|
||||
reveal alongside the conversation with a close affordance (T-233).
|
||||
- Image cards in the Claude conversation log: `clide image show <path>
|
||||
[--caption …]` renders an image inline (PNG/JPG/JPEG/GIF/WebP/BMP),
|
||||
clide-owned and display-only (D-78). The path is resolved workspace-relative
|
||||
and must exist; the verb registers in the dispatcher so it shows up in `clide
|
||||
capabilities`. (T-249)
|
||||
- `clide capabilities` lists the live command surface as JSON (every verb with
|
||||
its subsystem + argument schema), reflected from the dispatcher so it never
|
||||
drifts. A new `/clide` skill points Claude at it for discovery. (T-248)
|
||||
- Toast notifications for operation feedback: non-modal cards slide in
|
||||
bottom-right, auto-dismiss (errors linger), stack, and are manually
|
||||
dismissable, with success/warning/error/info severities. Components raise
|
||||
them by publishing to the kernel MessageBus — git push/pull show the first
|
||||
ones. (T-50)
|
||||
- `clide ui toast "message" [--severity …] [--duration MS]` raises a toast in
|
||||
the live GUI from the CLI — so an agent or script can surface "done/failed"
|
||||
on your screen. The drive-half complement to the toast system. (T-245)
|
||||
- Claude pane folds runs of tool calls/results into a collapsible "activity
|
||||
card" so prose isn't buried: collapsed by default with a live one-line ticker
|
||||
+ step count, click/Enter to expand. Claude prose, user messages, and failed
|
||||
results stay first-class; diffs and thinking stay visible at the default
|
||||
level. (T-230)
|
||||
- Theme switcher in the status bar: a far-right control showing the current
|
||||
theme that opens a popover to switch live — click or keyboard (arrows/Enter,
|
||||
Esc to dismiss). The `theme.pick` palette command is unchanged. (T-234)
|
||||
- Catppuccin Mocha theme, plus a high-contrast `catppuccin-mocha-hc` sibling,
|
||||
added to the bundled themes — switchable from the theme picker. Faithful to
|
||||
the official palette (D-69). (T-82)
|
||||
- `clide ui open <reader> <id|path>` opens a doc in a GUI reader from the CLI —
|
||||
`tickets`/`decisions` by id, `markdown` by path — so an agent can surface what
|
||||
it's looking at on your screen. The drive-half complement to `clide status`. (T-231)
|
||||
- Cycle Claude's permission mode from the primary pane: Ctrl/Cmd+M while the
|
||||
composer is focused, a clickable mode badge in the status line, or the
|
||||
"Claude: Cycle permission mode" palette command — steps default → accept-edits
|
||||
→ plan (bypass stays behind the cockpit's confirm). (T-226)
|
||||
- `clide status` — a one-shot orientation snapshot for agents: workspace, git
|
||||
summary, active editor buffer + selection, viewed reader docs, the live panes
|
||||
the user sees, and the layout. Previously an unknown command (exit 3). (T-221)
|
||||
- `clide pane list` now reflects the live GUI tabs the user sees (Claude, Files,
|
||||
Editor, viewers) alongside PTY panes — each with a stable id, slot, title, and
|
||||
active/visible state — by snapshotting the kernel panel layout at request time.
|
||||
Restores the D-6 "agent sees what the user sees" half of parity. (T-219, D-83)
|
||||
- Click empty Claude-pane area to focus the composer: a tap on conversation
|
||||
dead space lands the cursor in the input. Message controls and transcript
|
||||
text-selection are unaffected, and it stays inert while a prompt occupies the
|
||||
interaction zone. (T-227)
|
||||
- Claude composer prompt history (Claude-CLI-style): Up recalls previously-sent
|
||||
prompts once the caret reaches the first line, Down steps back to newer ones
|
||||
and restores your in-progress draft past the newest. Per session. (T-163)
|
||||
- clide-hosted Claude sessions are now bootstrapped to drive the IDE: each
|
||||
spawned session gets `CLIDE_SOCK`/`CLIDE_WORKSPACE` in its env and `clide` on
|
||||
its PATH, a system-prompt note telling it it is inside clide and how to use
|
||||
`clide …`, and a `Bash(clide:*)` allow rule so those calls aren't prompted.
|
||||
Applies to primary, secondary, fork, and teammate sessions. (T-214, D-83)
|
||||
- The `clide` CLI now ships on PATH: `make build` and `make install` compile
|
||||
the C client by default, and `make install` places it at `~/.local/bin/clide`
|
||||
on Linux and macOS (the GUI launches via its desktop entry). Previously no
|
||||
build produced the client and `install` symlinked the GUI runner. (T-209)
|
||||
- Vim keymap preset: a modal editor (normal/insert/visual) with hjkl/w/b/e
|
||||
motions, dd/dw/x/D/yy/p/cc/cw edits, counts (`5j`), visual-range d/y/c, and
|
||||
a status-bar mode indicator. Switch presets from the palette (`Keymap: Vim`
|
||||
/ `Keymap: Default`). Built on a new keymap key-sequence layer (D-82). (T-65)
|
||||
- Search-and-replace across the workspace: enter a replacement in the search
|
||||
panel to preview each rewritten line, then Replace all (regex capture groups
|
||||
supported). Guarded by a clean-git-tree gate — git is the undo — and a
|
||||
confirmation. New `search.replace` command (preview + apply). (T-53)
|
||||
- Find-in-files sidebar panel (Ctrl/Cmd+Shift+F): search the workspace with
|
||||
regex and case toggles plus include/exclude globs; results stream in grouped
|
||||
by file and clicking a match opens the editor at that line. (T-52)
|
||||
- Workspace content-search engine with `search.grep` / `search.cancel` commands:
|
||||
a pure-Dart, isolate-parallel grep (literal or regex, case + include/exclude
|
||||
glob filters) that streams matches and honours the `ignore_files:` chain. The
|
||||
engine is in-process — no ripgrep dependency (D-79). (T-52)
|
||||
- `editor.open` accepts an optional 1-based `line` to position the initial
|
||||
selection on open (backs find-in-files click-to-line). (T-52)
|
||||
- Quick-open file finder (Ctrl/Cmd+P): a fuzzy file picker overlay over the
|
||||
whole workspace, separate from the command palette. Empty query lists recent
|
||||
files; Enter opens `.md` in the markdown reader and other files in the editor.
|
||||
(T-51)
|
||||
- Workspace ignore now follows the `ignore_files:` list in `.pql/config.yaml`
|
||||
(ordered, later-wins, per D-4) instead of a hardcoded `.gitignore` +
|
||||
`.clideignore` pair — the single ignore knob clide owns. (T-52)
|
||||
- `files.walk` command — a recursive, ignore-pruned, capped flat file listing
|
||||
of the workspace, backing quick-open and search. (T-51, T-52)
|
||||
- Sidebar readers (markdown, decision, ticket) gain chrome: a pin/unpin toggle
|
||||
before the title (separate from navigation), plus a right-hand navigator
|
||||
(back, forward, jump-to-pin) and edit pencil. The ticket reader also joins the
|
||||
shared retained nav. (T-189, T-190, T-191, T-198, T-199)
|
||||
|
||||
### Changed
|
||||
|
||||
- **Every tool use is now a collapsible card** on one `ClideCollapserCard`
|
||||
primitive — activity / edit / sub-agent runs and each tool call (single = a
|
||||
one-item list). The collapsed ticker shows the label + echoed last line + a
|
||||
fixed-width count; the status tick hugs the right edge, the chevron the left;
|
||||
`color` drives the border + label. (T-305)
|
||||
- The Claude composer's **slash typeahead**, the team-chat **@-mention** list,
|
||||
and the status-bar **theme switcher** now ride the shared
|
||||
`ClideAnchoredOverlay` + `ClideMenu` popover primitive, alongside the menu
|
||||
bar. The @-mention list gains full keyboard nav (arrows/Enter), and both
|
||||
typeaheads narrow live as you type. (T-286, D-88)
|
||||
- Ticket cards now show parentage as a small **tree** — the parent as a muted,
|
||||
clickable breadcrumb above and the card's own ticket **bold** under a `└`
|
||||
connector — instead of the ambiguous inline `T-1 ← T-9` arrow. (T-281)
|
||||
- The in-flight **turn indicator** ("Pondering…") now renders in Claude's
|
||||
coral-orange brand accent instead of muted grey. (T-273)
|
||||
- A **sub-agent's prose and thinking** are now attributed to the **`agent`** (a
|
||||
muted stripe), not the main-thread coral **`claude`** — so a sub-agent's
|
||||
output is no longer presented as if the main Claude said it. Main-thread items
|
||||
are unchanged. (T-265)
|
||||
- A **sub-agent's whole run** — prose, thinking, and tool calls — now nests in
|
||||
an **`agent run` holder under its Agent card** instead of spilling loose into
|
||||
the main thread. It attaches via `parentUuid` (correct for parallel agents),
|
||||
and the redundant returned-result is no longer shown twice. (T-264)
|
||||
- The folded **activity card** now reads as one **container wrapping its
|
||||
sub-cards**, and you can collapse it by clicking anywhere on the holder's own
|
||||
background — not a top header that scrolls out of reach as a run streams. Taps
|
||||
on a sub-card (and its copy button) still hit that card; a focusable caret
|
||||
keeps the control keyboard/AT reachable. (T-266)
|
||||
- A **sub-agent prompt** is no longer mislabelled as your input: a sidechain
|
||||
prompt now reads as a muted **`agent prompt`** (never the blue `you`) and folds
|
||||
into its **Agent/Task card**, collapsed by default. The prompt attaches to the
|
||||
right card via `parentUuid`, so parallel agents in one turn stay correctly
|
||||
paired. The sub-agent's work stays visible after the call. (T-263)
|
||||
- A successful tool call now renders as **one merged card** instead of a
|
||||
separate call + result pair: a green check sits at the header's right edge and
|
||||
the output folds in as a colorized code block (Read → file grammar, Bash →
|
||||
shell) revealed on expand. Failures keep their prominent red card, now with a
|
||||
matching header mark. (T-262)
|
||||
- The in-flight turn indicator now feels alive: instead of a static gray
|
||||
`running…`, it shows a rotating curated status verb (`Pondering…`,
|
||||
`Conjuring…`, …) with an animated ellipsis. Respects reduced-motion (static
|
||||
verb) and keeps a stable a11y label. (T-255)
|
||||
- The `clide` CLI launch check now distinguishes a dev-tree build
|
||||
(`native/<plat>/clide`) from a packaged install — surfaced as an info note on
|
||||
a checkout rather than treated as a clean install or prompting a reinstall.
|
||||
(T-256)
|
||||
- Command palette (⌘⇧P) now fuzzy-matches command titles (subsequence, not
|
||||
just substring) and floats recently-used commands to the top. (T-23)
|
||||
- ⌘K now opens a **Settings** modal instead of a theme-only picker. Its first
|
||||
(currently only) section is Appearance — base themes, sorted, with a High
|
||||
contrast toggle for `-hc` siblings — matching the status-bar switcher. (T-238)
|
||||
- The pql search panel merged into the Search tab, which now has modes: Find
|
||||
(content grep), Vault (pql ranked search), Query (PQL DSL), and Markdown (the
|
||||
synced file listing). The standalone pql sidebar tab is gone; Backlinks stays
|
||||
in the context panel. (T-201)
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Tab strips get a hairline of breathing room.** The tab strip (Claude session
|
||||
tabs, slot tabs) butted flush against the chrome above it, reading as cramped;
|
||||
it now sits 1px below, the pane surface showing through the gap. (T-324)
|
||||
- **Re-showing an image after it changes on disk now refreshes.** Image cards,
|
||||
thumbnails, the lightbox, and `clide image show` keyed Flutter's image cache by
|
||||
path alone, so overwriting a file in place showed the stale render. A new
|
||||
`ClideFileImage` folds mtime + size into the key, so an in-place change
|
||||
re-decodes. (T-312)
|
||||
- **The `context` / `thinking` / agent-prompt blocks are now carded like the
|
||||
rest.** These muted meta blocks rendered frameless, reading as unfinished
|
||||
`> context …` rows between the framed tool cards. They now sit in a bordered
|
||||
card — still muted, collapsed by default, with a first-line summary and a left
|
||||
chevron. (T-306)
|
||||
- **Numpad digits now pick permission/question options too.** The prompt card's
|
||||
number-key shortcuts only matched the top number row; numpad `1`-`9` now map to
|
||||
the same 1-9 selection, so the keypad works for Allow/Deny and question options.
|
||||
The note-field guard still lets digits type normally when a note is focused.
|
||||
(T-310)
|
||||
- **The composer no longer jams against the window bottom when the status bar
|
||||
is hidden.** With the bar gone, the bottom-most pane content used to run flush
|
||||
into the window's resize-drag edge; the layout now reserves that edge so the
|
||||
input box bottom-anchors consistently whether or not the status bar shows.
|
||||
(T-298)
|
||||
- **Open Workspace no longer spews `GLib-GIO-CRITICAL` to the console.** The
|
||||
folder picker now uses the portal-backed `GtkFileChooserNative` (out-of-process
|
||||
in sandboxed/Flatpak builds), and a narrowly-scoped GLib log filter swallows the
|
||||
known-benign `g_file_info_get_size … without standard::size` message GTK's
|
||||
file-chooser sidebar emits internally on every pick — every other GLib-GIO
|
||||
critical still surfaces. (T-287)
|
||||
- **The conversation re-anchors when the input area resizes.** Opening a
|
||||
permission prompt or AskUserQuestion (which grows the bottom zone, D-78) no
|
||||
longer hides the last message behind it — when pinned to the tail, the view
|
||||
re-scrolls to keep it visible; a scrolled-up reader is left undisturbed.
|
||||
(T-297)
|
||||
- **The chosen theme now persists across restarts**, per repo. Picking a theme
|
||||
(status-bar switcher or Settings) writes it to the repo's
|
||||
`.clide/settings.yaml` (and a global default), and reopening the repo restores
|
||||
it — including the high-contrast variant. A removed theme falls back to the
|
||||
default instead of resetting silently. (T-293)
|
||||
- Folded activity and agent-run cards in the Claude conversation now use the
|
||||
same bottom spacing as the prose cards around them, instead of sitting
|
||||
cramped 3px below the next card. (T-282)
|
||||
- The welcome screen no longer overflows on a short or narrow window — its
|
||||
content scrolls when it can't fit and stays centred when it can, and a long
|
||||
git-branch name on a recent-project row now truncates with an ellipsis. (T-273)
|
||||
- Bordered conversation cards (tool / Agent calls) now use the same interior
|
||||
vertical padding (8) as the stripe cards, so a collapsed tool/Agent card no
|
||||
longer reads chunkier — taller box, more trailing space — than its
|
||||
neighbours in the conversation log. (T-282)
|
||||
- Conversation cards no longer **mis-associate their state** when the message
|
||||
list reshapes as a tool result streams in. The list items now carry stable
|
||||
per-item keys, so a card you expanded (or its hover/cluster state) stays
|
||||
pinned to its own message instead of jumping to a neighbour when a read/write
|
||||
completes and folds its result in. (T-285)
|
||||
- The status-bar footer marquee now **honours reduced motion**: when the OS
|
||||
reduce-motion setting (`MediaQuery.disableAnimations`) is on, a long status
|
||||
line no longer scrolls — it renders statically (clipped) — matching the turn
|
||||
indicator, which already obeyed the flag. Unifies the two animations on one
|
||||
mechanism and removes a `pumpAndSettle` hang the perpetual ticker caused.
|
||||
(T-284)
|
||||
- Switching the workspace in place (Open Project/Folder) now rebinds the Claude
|
||||
pane to the new repo's session instead of keeping the previous repo's
|
||||
conversation, and drops the old repo's secondary tabs. Separate windows were
|
||||
already isolated — this only affected reusing one window for another repo.
|
||||
(T-269)
|
||||
- `/clear` in the primary Claude pane now clears that session **in place** —
|
||||
it empties the pane's deterministic, restart-stable session instead of
|
||||
starting a throwaway random one. Previously a cleared primary was orphaned:
|
||||
the next launch re-resolved to the deterministic id and resumed the
|
||||
pre-clear conversation, so the clear silently didn't stick. Secondary panes
|
||||
keep their fresh-session behaviour. (T-268)
|
||||
- In Vim mode, Esc in insert/visual mode returns to normal mode instead of
|
||||
closing the editor. The global "exit focus / close editor" Esc binding now
|
||||
stands down while Vim is in insert or visual mode. (T-257)
|
||||
- The permission-mode badge and Ctrl/Cmd+M now visibly cycle the mode in the
|
||||
status line. The mode was changed on the session but never reflected back, so
|
||||
both looked dead. (T-250)
|
||||
- Recent-project rows (welcome screen and the project switcher) now ellipsize a
|
||||
long path instead of overflowing the row — a long repo path no longer spills
|
||||
past the edge. (T-122)
|
||||
- CLI commands that take arguments now work: `clide editor open <path>`,
|
||||
`clide files read <path>`, `clide pane focus <id>` / `resize <id> <c> <r>`,
|
||||
etc. now bind positional/flag argv to the handler's named args (they
|
||||
previously returned "X is required" from the CLI). (T-232)
|
||||
- The Claude composer no longer loses a half-typed message when the UI changes
|
||||
under it — e.g. a permission prompt taking the composer's place. The draft
|
||||
(text and caret) is kept per session and restored when the composer returns.
|
||||
(T-228)
|
||||
- File watcher no longer emits change events for files inside ignored
|
||||
directories (`.dart_tool/`, `build/`, etc.): it now checks ancestor dirs,
|
||||
not just the leaf. Most visible on macOS, where FSEvents delivers the nested
|
||||
creates that inotify usually drops.
|
||||
- The Claude sidebar's Activity / Team / Config sub-tabs are now keyboard-
|
||||
activatable: they were pointer-only (raw `GestureDetector`), so Tab traversal
|
||||
skipped them and Enter/Space did nothing. They now use `ClideTappable`
|
||||
(focusable, Enter/Space → activate) and carry button + selected semantics.
|
||||
(T-182)
|
||||
- The default keymap is no longer silently disabled at startup: `default.yaml`
|
||||
bound Tab/Shift+Tab to undefined `focus.next`/`focus.previous` intents, which
|
||||
made the loader drop the entire preset (palette, quick-open, find-in-files,
|
||||
zoom). Those are now real focus-traversal intents, and a test parses every
|
||||
shipped preset so a typo fails CI instead. (T-204)
|
||||
- Opening the editor split no longer floods exceptions: the resize handle's
|
||||
slider semantics now carry increased/decreased values, and the Claude pane
|
||||
keeps a stable identity across the reparent so its text-selection region
|
||||
isn't torn down mid-update. (T-203)
|
||||
- A `rate_limit_event` whose `resetsAt` is a numeric epoch no longer crashes the
|
||||
Claude session — it was cast as a string. (T-202)
|
||||
- Filter/search inputs show their hint as visible placeholder text, and the
|
||||
search-glass icon is now optional — so the Search tab's Find fields (search,
|
||||
replace, include/exclude globs) are distinguishable instead of four identical
|
||||
empty boxes. (T-201)
|
||||
- The sidebar icon rail no longer overflows when there are more tabs than fit:
|
||||
it centers the icons when they fit and scrolls horizontally otherwise. (T-200)
|
||||
- The editor pane now opens over the Claude pane when a file is opened — the
|
||||
reader's edit pencil, a file-tree click, or a decision's edit all reveal the
|
||||
editor tab now (it was contributed but never activated). (T-197)
|
||||
- Clicking a decision opens it on the first click. The right-pane readers
|
||||
(markdown + decisions) now share a retained back/forward nav history that
|
||||
survives the tab switch, so the selection that reveals a reader is no longer
|
||||
lost before the widget subscribes; back/forward re-emit through that history.
|
||||
(T-196)
|
||||
- The markdown reader can now open user-scope Claude config files (skills /
|
||||
agents / commands under `~/.claude`), not just repo-local ones. `files.read`
|
||||
gained a read allow-list covering the workspace plus the trusted Claude config
|
||||
roots; writes stay repo-confined and off-root paths are still rejected. (T-195,
|
||||
D-80)
|
||||
- The markdown reader opens files given an absolute path again (e.g. a skill's
|
||||
`SKILL.md` from the Claude Config tab). `resolveUnderRoot` no longer doubles an
|
||||
absolute path onto the workspace root; absolute-under-root resolves, while
|
||||
paths outside the root are still rejected. (T-194)
|
||||
- The composer slash typeahead now lists clide-owned commands — `/resume` and
|
||||
`/fork` (and `/clear`) surface even though the CLI probe doesn't advertise
|
||||
them, unioned onto whatever command source the composer uses. (T-162)
|
||||
- Clicking a decision opens it in the decision reader again — the decisions
|
||||
extension no longer tears down and re-contributes its panel tab on every
|
||||
selection; it activates a static tab and reveals the panel like the ticket
|
||||
panel. (T-188)
|
||||
- Clicking a markdown file opens it in the right-side markdown reader again —
|
||||
the files panel, the Claude Config tab, and wiki `.md` links now publish to
|
||||
the reader instead of the editor. (T-187)
|
||||
- A forked Claude session now reports its real session id (captured from the
|
||||
branch's `init` event) instead of the placeholder it was spawned with, so a
|
||||
fork can itself be resumed/forked. (T-185)
|
||||
- Claude replies now stream token-by-token. The `--include-partial-messages`
|
||||
output arrives as `stream_event` deltas (not `assistant`+`partial:true` as
|
||||
first assumed), so the previous handler never fired; the session now reads the
|
||||
real shape, growing a placeholder in place and finalizing it from the matching
|
||||
`assistant` event. (T-184)
|
||||
- Claude conversation card actions (copy + custom) are now keyboard-focusable
|
||||
and always reachable — revealed on hover or focus, activatable by Tab +
|
||||
Enter/Space, with Semantics labels for assistive tech (T-174).
|
||||
- Status bar no longer overflows when the focused-pane context line is long
|
||||
— the in-pane slot now takes a flexible share of the bar and marquee-scrolls
|
||||
within it instead of pushing the row past its width.
|
||||
- Write/Edit permission cards no longer print the file path twice — the
|
||||
description line is suppressed when it just repeats `file_path`.
|
||||
- Resumed Claude session no longer starts with an empty pane — `claude
|
||||
--resume` carries Claude's prior context but emits no past turns over
|
||||
stream-json, so the orchestrator now seeds the conversation by reading
|
||||
the tail (up to 256 KB) of the transcript JSONL on disk.
|
||||
|
||||
### Changed
|
||||
|
||||
- Claude conversation tool cards are now typed — Edit/Write render a diff,
|
||||
Bash shows the command and its output, Read/Grep show the file/query, and
|
||||
each tool result pairs back to its call to render the diff or error in
|
||||
place instead of an indented JSON dump (T-168).
|
||||
- Claude status line reflects live session state from stream-json events —
|
||||
model, permission mode, context size, and turn cost come straight off the
|
||||
init/result events rather than a separate config probe (T-168).
|
||||
- Claude session persistence now rides on `claude --resume` instead of tmux
|
||||
(T-167, amends D-41). A restart resumes the primary session, `/clear`
|
||||
starts a fresh one, and `/resume` reopens a picked session — all without
|
||||
tmux.
|
||||
- Permission prompt cards render the tool input in the shape that fits the
|
||||
tool — Bash shows the command as a shell code block (with a footer for
|
||||
`run_in_background` / `timeout`), Write shows the path plus the content
|
||||
highlighted from its extension, Edit shows the path plus before/after
|
||||
blocks. Unknown tools fall back to the indented-JSON dump.
|
||||
- Claude meta sidebar is now tabbed — Activity / Team / Config (T-182).
|
||||
Activity shows usage stats plus the primary session's live runtime; Team
|
||||
holds the roster and auto-fronts when a team spawns; Config shows the
|
||||
environment settings table. Activity and Config share one table geometry so
|
||||
switching doesn't jump.
|
||||
|
||||
### Removed
|
||||
|
||||
- tmux is no longer used for Claude sessions (T-167) — the tmux session
|
||||
lifecycle and the tmux-polling team observer are gone, replaced by the
|
||||
managed-session orchestrator. tmux is still used for the general-purpose
|
||||
terminal pane.
|
||||
|
||||
### Added
|
||||
|
||||
- Claude team cockpit — the meta sidebar's Team tab gains live controls for
|
||||
clide-managed agents: show/hide, mute, close, and inject-a-message per
|
||||
roster row, plus a live shared task list with reassign. Each action has a
|
||||
matching `clide` command (D-6 parity). (T-171, D-77)
|
||||
- Per-agent permission-mode badge in the cockpit roster — click cycles the
|
||||
safe trio default → acceptEdits → plan and sends `set_permission_mode` to
|
||||
that session; Shift-click reaches `bypassPermissions` behind a confirm.
|
||||
The badge reflects the live mode. (T-181, D-77)
|
||||
- Fork a Claude conversation into a new pane — `/fork` (or a roster Fork
|
||||
button / `clide.agent.fork`) branches a session via `--resume … --fork-session`,
|
||||
opening an independent continuation that leaves the original untouched. (T-172, D-77)
|
||||
- Team chat inbox — broker traffic renders as a chat timeline (colour-coded
|
||||
sender chips), in a compact cockpit widget that pops out to a full pane. The
|
||||
user is a first-class participant: post with `@name` routing (or broadcast),
|
||||
with an interrupt tickbox that cancels the target's turn before delivery. (T-180, D-77)
|
||||
- Config sidebar tab — a pinned settings table plus expandable, never-truncated
|
||||
sections for skills / agents / commands / hooks / permissions (colour-coded by
|
||||
kind) / MCP servers; file-backed entries open their `.md` in the reader. ClaudeConfig
|
||||
now also surfaces agents, hooks, MCP servers, and file paths. (T-183, D-76)
|
||||
- Team coordination broker (T-170, D-77) — clide hosts an in-process MCP
|
||||
server (`clide-team`) for managed sessions over the stream-json control
|
||||
channel, giving agents tools to message each other, broadcast, see the
|
||||
roster, read an inbox, and share a task list. Each agent's role and the
|
||||
roster are injected into its system prompt.
|
||||
- Interrupt a running Claude turn (D-78) — Escape in the composer (when no
|
||||
typeahead is open) or a Stop button shown while busy cancels the current
|
||||
turn over the stream-json control channel. The escape hatch from a
|
||||
runaway turn.
|
||||
- Native permission & AskUserQuestion prompts (T-166, T-175, T-176, T-179,
|
||||
D-78) — the composer becomes a prompt: Allow / Allow-and-don't-ask-again /
|
||||
Deny showing the command, or an AskUserQuestion option picker (single or
|
||||
stepped, with "Other" free-text and per-choice notes). Closes the tmux
|
||||
prompt gap.
|
||||
- Conversation message cards (T-173) — every turn in the Claude pane now
|
||||
renders through one card template with a copy button on hover and a
|
||||
collapse/expand caret for tool calls, results, and thinking.
|
||||
- Collapsed-by-default tool cards (T-177) — multi-line tool calls and
|
||||
results start collapsed behind a one-line summary; one-line output stays
|
||||
inline so a caret never hides a single line.
|
||||
- Prompted tool calls are quieter in the log (T-179) — a permission request
|
||||
shows the command in the prompt, not as a raw tool-use card; once decided
|
||||
it collapses to a one-line summary with a green (approved) or red (denied)
|
||||
border, and the result is kept. AskUserQuestion's tool-use + result are
|
||||
replaced by the logged answer.
|
||||
- Harness-injected messages are de-emphasized (T-178) — skill loads,
|
||||
slash-command expansions, and system reminders (Claude's `isSynthetic`
|
||||
messages) render as a muted, collapsed "context" card instead of a blue
|
||||
"you" message, since they weren't typed by the user.
|
||||
- Claude meta sidebar (T-141, T-157) — an always-pickable left-panel tab
|
||||
showing Claude activity (the latest day's messages/sessions/tool-calls
|
||||
plus lifetime totals, from `stats-cache.json`) and, when a tmux team is
|
||||
running, a roster of its members (colour · name · agent type · model)
|
||||
with each member's live permission-mode and context once it's active.
|
||||
- Claude session storage view (T-148) — the `claude.session-storage`
|
||||
command opens a modal listing the workspace's session transcripts with
|
||||
their on-disk sizes and a total, each removable with a two-click
|
||||
confirm. User-driven only; clide never deletes transcripts on its own.
|
||||
- Slash-command typeahead in the Claude composer (T-152) — typing `/`
|
||||
(anywhere in the message, not just at the start) pops a list of
|
||||
matching commands and skills sourced from the Claude environment;
|
||||
arrow keys move, Enter/Tab completes, Escape dismisses.
|
||||
- Claude environment service (T-151) — clide reads skills, commands,
|
||||
settings, and permissions from `~/.claude` and the repo's `.claude`
|
||||
(layered), and caches the slash-command list per claude version. Backs
|
||||
the typeahead and command-aware send.
|
||||
- Per-session status in the bottom status bar (T-145, T-150, T-154) — the
|
||||
active Claude pane shows its model · permission mode (accept-edits /
|
||||
plan / …) · context-token count · configured skills count, swapping to
|
||||
the focused pane on tab switch and clearing on blur. Long status text
|
||||
marquee-scrolls within the slot.
|
||||
- tmux agent teams surface as native teammate tiles (T-139, T-140) —
|
||||
when a Claude team is running, each teammate shows as a live
|
||||
conversation tile beside the lead in a grid that wraps 1→2→3 columns,
|
||||
with a resizable split. Identity and lifecycle come from the team
|
||||
config; per-teammate content streams over the MessageBus.
|
||||
- Native composer in the Claude pane (T-138) — type below the
|
||||
conversation and press Enter to send (Shift+Enter for a newline).
|
||||
Submits via the tmux server (bracketed paste + Enter), so input
|
||||
reaches Claude even when no tmux client is attached; multi-line goes
|
||||
as one message.
|
||||
- File and image paste in the composer (T-138, T-142) — Ctrl/Cmd+V of a
|
||||
copied file or clipboard image adds a removable chip (image thumbnail
|
||||
or file icon) above the input; on send its `@path` is appended to the
|
||||
message. Plain text pastes inline. Backed by a native `clide/clipboard`
|
||||
channel (GTK + macOS).
|
||||
- Claude pane renders natively from the transcript (T-137, D-75) — the
|
||||
conversation shows as native cards (user / assistant markdown /
|
||||
thinking / tool-use / result) instead of a terminal, with text
|
||||
selection + copy across cards. Claude still runs in tmux; the terminal
|
||||
builtin stays for general use.
|
||||
- Multi-file editor tabs — the editor pane now shows one tab per open
|
||||
buffer (filename + a dot when unsaved) via the shared tab strip;
|
||||
opening a second file no longer replaces the first. Click a tab to
|
||||
switch, × to close. Backed by the daemon's existing multi-buffer
|
||||
model.
|
||||
- Typed IPC command-schema framework (T-119/T-120, D-74) — commands
|
||||
register an argument schema beside their handler; the dispatcher
|
||||
normalises argv into named args, coerces types, and validates
|
||||
(charset, leading-dash, ranges, caps) before the handler runs.
|
||||
- `clide panel resize <slot>` CLI verb (T-119) — set an absolute size
|
||||
with `--to` or nudge with `--by`; `editor` targets the split ratio.
|
||||
Completes user/Claude parity (D-6) with T-111's keyboard resize.
|
||||
- Unix-domain IPC socket server in the Flutter app (T-99 / T-124).
|
||||
Per-workspace path (D-70: `$XDG_RUNTIME_DIR/clide/<hash>.sock` on
|
||||
Linux, `~/Library/Caches/clide/<hash>.sock` on macOS). 0600 socket
|
||||
+ 0700 parent (D-71). Multi-connection accept loop with serial
|
||||
dispatch through `DaemonDispatcher` (D-72). Foundation for the C
|
||||
`clide` client (T-126) and MCP (T-130). No client yet — testable
|
||||
via `socat - UNIX-CONNECT:$SOCK`.
|
||||
- argv→IpcRequest translator (`lib/src/cli/argv_to_request.dart`) —
|
||||
parses `clide SUBSYSTEM VERB [pos...] [--flag] [-- passthrough]` and
|
||||
the umbrella commands (`status`, `tail`, `version`, `ping`) per D-6
|
||||
into the wire envelope. Pure Dart; lets the C client (T-126) stay a
|
||||
dumb pipe (T-99 / T-125).
|
||||
- `DaemonClient.reconnectAt(newPath)` — swap an active client onto a
|
||||
different socket without restart (project switch in T-127).
|
||||
- Event streaming over the IPC socket (T-99 / T-129) — `clide tail
|
||||
--events [--filter X]` opens a long-lived subscription, replays up
|
||||
to 16 recent matching events per subsystem (D-6), and streams new
|
||||
ones as JSON lines. C client loops on `data.streaming` ack. Slow /
|
||||
broken subscribers drop themselves without blocking the bus.
|
||||
- MCP server over HTTP+SSE (T-99 / T-130, per D-68 / D-73). Localhost
|
||||
HTTP listener advertises via `$HOME/.claude/ide/<pid>.lock` so
|
||||
Claude Code's `/ide` discovers it. JSON-RPC 2.0 with the two
|
||||
minimum `/ide` tools shipped as stubs
|
||||
(`mcp__ide__getDiagnostics`, `mcp__ide__executeCode`); real
|
||||
implementations follow.
|
||||
- C `clide` shell client at `native/clide-cli/clide.c`. Walks CWD up
|
||||
to the git root, hashes to the per-workspace socket (D-70), ships
|
||||
argv. `make clide-cli` builds it; on PATH, `clide status` works
|
||||
from any clide-workspace directory once the app is up (T-99,
|
||||
T-126).
|
||||
- Startup project picker — clide now opens to the welcome screen by
|
||||
default instead of auto-opening the last project. A per-row
|
||||
"always open this project on launch" checkbox in welcome's RECENT
|
||||
list sets a sticky-startup flag; if exactly one project has it,
|
||||
that one opens directly. Two or more, or none ⇒ picker (T-115).
|
||||
- CONTRIBUTING.md "Running clide from the shell" section — documents
|
||||
the shell verbs, exit-code contract per D-68, and Claude Code
|
||||
`/ide` MCP discovery via `~/.claude/ide/<pid>.lock` (T-131).
|
||||
|
||||
### Changed
|
||||
|
||||
- The empty Claude pane now shows a native startup banner — clide logo,
|
||||
session role, workspace, and tmux status — instead of a bare "Waiting
|
||||
for Claude…" (T-149).
|
||||
- User and Claude turns in the conversation now render as distinct
|
||||
accent-striped cards over a filled background — your prompts in the
|
||||
theme focus colour, Claude's replies in Claude's brand orange
|
||||
(T-143, T-144).
|
||||
- Claude conversation content now flows through the kernel MessageBus —
|
||||
a reader tails the transcript and publishes items; the pane subscribes.
|
||||
Decouples reading from rendering so the upcoming team panels can show
|
||||
one lead plus a tile per teammate (T-137).
|
||||
- In-process IPC dispatch swapped for socket loopback (T-127). The
|
||||
Flutter UI's `DaemonClient` now talks to its own `IpcServer` over
|
||||
the same per-workspace Unix socket the C `clide` client uses — one
|
||||
transport, one contract.
|
||||
- D-56 / D-68 amended with implementation notes — both decisions now
|
||||
link out to T-99's eight slices (T-124–T-131) and the D-70/71/72/73
|
||||
records they spawned (T-131).
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
- `lib/kernel/src/ipc/in_process.dart` (`InProcessClient`) — replaced
|
||||
by the socket-loopback `DaemonClient` (T-127).
|
||||
- `lib/kernel/src/backend.dart`, `lib/kernel/src/backend_entry.dart`,
|
||||
`lib/kernel/src/ipc/isolate_client.dart` — the third unused IPC
|
||||
path (a backend-isolate model that was never wired through). Only
|
||||
the socket model survives now (T-128).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Claude pane is no longer dead-on-arrival when resuming a session (T-161).
|
||||
The primary pane (and `/resume`) relaunched Claude with `--session-id
|
||||
<existing-id>`, which Claude rejects as "already in use" — so the pane had
|
||||
no live backend and typed input vanished. clide now uses `--resume` for an
|
||||
existing session and `--session-id` only for a brand-new one.
|
||||
- Claude pane no longer floods the console with "markNeedsBuild called
|
||||
during build" (T-159) — a focused pane surfacing its status-bar widget
|
||||
now defers the notification out of the build phase instead of rebuilding
|
||||
the status item mid-build.
|
||||
- The git / tickets / decisions / pql / problems tabs no longer log
|
||||
`i18n: namespace not registered` on boot (T-155). An extension's
|
||||
localized tab title is now loaded automatically on activation, and the
|
||||
five missing catalogs were added.
|
||||
- Slash commands sent from the Claude composer now actually run (T-153).
|
||||
Recognised commands are delivered as typed input so Claude's TUI parses
|
||||
them; other input (and stray leading slashes like a `/tmp` path) stays
|
||||
bracketed-pasted as literal text.
|
||||
- `/clear` is now handled by clide — it resets the Claude pane to a fresh,
|
||||
empty session — instead of being forwarded to Claude Code, whose `/clear`
|
||||
forked to a new session clide couldn't follow and left the pane
|
||||
unresponsive (T-156).
|
||||
- `/resume` is now handled by clide too (T-156): it opens a picker of the
|
||||
workspace's past sessions — each labelled by its first … last user message
|
||||
and when it was last active — and re-binds the pane to the chosen one,
|
||||
instead of forwarding Claude Code's session-forking `/resume`.
|
||||
- Claude secondary panes no longer flash a false "session exited" while
|
||||
the session is alive (a transient tmux client exit is now verified
|
||||
against the live session), and the tab and banner agree on the label
|
||||
("session N") (T-149).
|
||||
- Claude pane no longer gets stuck on "Waiting for Claude…" when its
|
||||
session can't be bound (T-147) — e.g. a session left over from before
|
||||
session-id binding, or a fresh machine. It now retires that stale
|
||||
clide session and starts a clean one. Only clide's own `-L clide`
|
||||
sessions are touched (never a terminal Claude, never transcript files).
|
||||
- Secondary Claude tabs showed the primary's conversation instead of
|
||||
their own (T-146). Each pane now binds to its own session via
|
||||
`claude --session-id`, so concurrent sessions in one workspace stop
|
||||
colliding on the newest transcript — the primary keeps a stable id
|
||||
(resumes), secondaries get a fresh one (clean session).
|
||||
- Claude pane no longer freezes the app on open — the transcript reader
|
||||
caps its initial read to the recent tail, parses off the UI isolate,
|
||||
and coalesces view notifications into one rebuild per burst (T-137).
|
||||
- Daemon-not-connected on startup — panels and the Claude pane raced
|
||||
the socket loopback. Requests now wait briefly for an in-flight
|
||||
connection, the server isn't restarted for the same workspace, and
|
||||
the client connects once instead of twice.
|
||||
|
||||
### Security
|
||||
|
||||
## [2.1.0] — 2026-05-18
|
||||
@@ -91,12 +888,33 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
|
||||
|
||||
### Changed
|
||||
|
||||
- Terminal mouse wheel forwards as proper xterm wheel-button escapes
|
||||
when the inner program declares a mouse mode (?1000h / ?1002h /
|
||||
?1003h, optionally +?1006h SGR). Falls back to PgUp/PgDown only
|
||||
when no mouse mode is active. vim mouse=a / htop / less mouse modes
|
||||
now react to the wheel (T-74).
|
||||
- `lib/src/terminal/` cleaned to the project bar — commented-out
|
||||
`print()` debugging stubs stripped from `custom_text_edit.dart`,
|
||||
stale TODOs in `parser.dart` + `keytab.dart` replaced with clear
|
||||
"not implemented" notes (G2/G3 charsets, VT52 records), and the
|
||||
one `// ignore: invalid_use_of_protected_member` in
|
||||
`terminal_view.dart` gets an inline reason explaining why
|
||||
TerminalView owns its own ShortcutManager. Parser split deferred
|
||||
to T-123 (T-107).
|
||||
- Window-control close-button red, white close glyph, and palette
|
||||
ambient shadow are now tokens (`windowControl.closeHover*`,
|
||||
`shadow.ambient`) instead of hard-coded hex. Light themes get a
|
||||
softer ink-tinted shadow (T-114).
|
||||
- Text-zoom (Ctrl +/-/0) is now a kernel `TextZoom` service and shows
|
||||
up in the palette as `View: Zoom In/Out/Reset Zoom` (T-114).
|
||||
- `make gen-build-info` bakes `lib/src/build_info.g.dart` (name,
|
||||
tagline, version, repository, commit, date) and rewrites
|
||||
`assets/licenses.yaml` `self.version:` from `pubspec.yaml` on every
|
||||
build/run/test target. Welcome banner / status line / window title
|
||||
/ project switcher labels all read from those constants — one
|
||||
source of truth, no manual sync, no `--dart-define` plumbing.
|
||||
New `tagline:` field in pubspec for the short user-facing line
|
||||
(welcome subtitle, future web meta).
|
||||
- Panel splitters (sidebar / context / editor-split) are tab-focusable;
|
||||
arrow keys nudge by 10 px, Shift+arrow by 50 px (2% / 10% for the
|
||||
editor split). Exposed as slider Semantics nodes so screen readers
|
||||
@@ -168,6 +986,15 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Welcome status line no longer overflows on narrow viewports —
|
||||
whole-row `FittedBox(scaleDown)` instead of fixed sibling widths.
|
||||
Inline `fontSize:` literals replaced with the typography
|
||||
constants. Version label reads `clideVersion` so the status line
|
||||
stays in sync with `pubspec.yaml` (T-116).
|
||||
- Integration test `theme_picker_test.dart` no longer deadlocks —
|
||||
was `await`ing `services.commands.execute('theme.pick')` whose
|
||||
Future doesn't complete until the dialog is dismissed. Now
|
||||
fire-and-forget around `pumpAndSettle` (T-116).
|
||||
- `TerminalView.onTapUp` now actually fires on primary tap — was
|
||||
wired to a dead code path (T-93). Dead `onTapUp` surface on
|
||||
`TerminalGestureHandler` / `TerminalGestureDetector` removed.
|
||||
|
||||
@@ -76,6 +76,21 @@ make clean # remove build artefacts
|
||||
|
||||
One-time setup on a fresh clone: `make hooks && flutter pub get` once Flutter is installed.
|
||||
|
||||
### Tooling discipline
|
||||
|
||||
The `make` targets above are the entry points — run them, not the scripts they wrap. Check the changelog with `make changelog-gate`, never `ci/changelog_gate.sh` directly; same for `analyze`/`format`/`test`/`push-check`. The `make` layer sets up the environment and stays correct if a script moves.
|
||||
|
||||
Shell hygiene (keeps commands inside the permission allowlist, so they don't get denied mid-task):
|
||||
- **Working directory is the repo root already** — don't prepend `cd /…/clide` or pass `git -C`. Just run the command.
|
||||
- **One command per invocation** — no `&&`/`;` chaining and no multiple greps/echos in one call. The only exception is the `git commit -F` HEREDOC.
|
||||
- Prefer the Read/Edit/Grep tools over `cat`/`sed`/`grep` for inspecting files.
|
||||
|
||||
## Git workflow
|
||||
|
||||
Commit and push directly to `main` for routine work — this is a solo-dev repo and does not use a branch-first / feature-branch flow. Do **not** create a working branch just to land a change. (This overrides the generic "branch before committing on the default branch" assistant default.) The usual safety rules still hold: never `--no-verify`, never force-push `main`, and let the pre-push gate run.
|
||||
|
||||
The pre-commit hook auto-exports and stages `.pql/changelog/` (the pql ticket DB) on every commit — don't hand-stage it. A ticket change only persists if the turn makes at least one commit; with no commit the hook never fires and a later branch switch can drop it.
|
||||
|
||||
## Changelog discipline
|
||||
|
||||
[Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/). Every user-visible commit adds an entry under `## [Unreleased]` in [`CHANGELOG.md`](CHANGELOG.md). Cutting a release means moving Unreleased entries under a new dated version heading **and** bumping `pubspec.yaml` `version:` in the same commit — see [`.claude/skills/git-commit/SKILL.md`](.claude/skills/git-commit/SKILL.md) for the full rule.
|
||||
|
||||
@@ -17,21 +17,24 @@ cd clide
|
||||
make hooks && flutter pub get
|
||||
```
|
||||
|
||||
`make hooks` installs the repo's git hooks (pre-commit + post-merge).
|
||||
Flutter must be on the stable channel and on `$PATH`.
|
||||
`make hooks` points `core.hooksPath` at [`.githooks/`](.githooks). The
|
||||
load-bearing one is **pre-push** (runs `make push-check`); the rest
|
||||
(pre-commit, post-checkout/merge/rewrite) are housekeeping. Flutter must
|
||||
be on the stable channel and on `$PATH`.
|
||||
|
||||
If you haven't used [pql](https://github.com/postmeridiem/pql) before,
|
||||
install it and run `pql init` once in the repo root. pql is a hard
|
||||
dependency for the governance + ticket workflow described below.
|
||||
|
||||
## The five commands you'll actually use
|
||||
## The commands you'll actually use
|
||||
|
||||
```
|
||||
make run # launch the desktop app
|
||||
make verify # no-tests sweep — analyze + format + decisions + changelog gate
|
||||
make test # fast suite — analyze + format + unit + widget + golden
|
||||
make test # fast dev loop — analyze + format + unit + widget + golden, NO coverage, parallel (~20s)
|
||||
make test-coverage # same suite WITH coverage (writes coverage/lcov.info; used by the gate)
|
||||
make test-a11y # WCAG-AA contrast + keyboard traversal contracts
|
||||
make push-check # the pre-push gate; what CI runs
|
||||
make push-check # the pre-push gate; what CI runs (runs test-coverage, not the no-coverage test)
|
||||
make build-linux # release artefact for the host platform
|
||||
```
|
||||
|
||||
@@ -49,13 +52,91 @@ commit. Pre-push includes:
|
||||
|
||||
- `flutter analyze` (zero warnings)
|
||||
- `dart format --set-exit-if-changed`
|
||||
- the fast unit/widget/golden suites
|
||||
- accessibility contract tests
|
||||
- the core (`dart test`) suite
|
||||
- the unit/widget/golden/a11y suites, instrumented for coverage
|
||||
(`make test-coverage` — the a11y suite runs here, not as a separate pass)
|
||||
- a coverage floor read from `coverage_floor:` in `pubspec.yaml`
|
||||
(currently 95 %; ratchets up only — see
|
||||
[D-66](governance/decisions/testing.md#d-66))
|
||||
- `CHANGELOG.md` `[Unreleased]` bullets ≤ 60 words each
|
||||
|
||||
## The C `clide` shell client
|
||||
|
||||
`clide` (the binary) is a ~250 LOC C program in
|
||||
[`native/clide-cli/clide.c`](native/clide-cli/clide.c) that talks to
|
||||
the running Flutter app's IPC socket so Claude (and you) can drive
|
||||
clide from any shell. It walks CWD up to the workspace's `.git`,
|
||||
computes the same FNV-1a 64-bit hash the Dart side uses (per D-70),
|
||||
opens the per-workspace socket, and sends argv across the wire under
|
||||
a sentinel `_argv` cmd. The argv parser lives in Dart
|
||||
([`lib/src/cli/argv_to_request.dart`](lib/src/cli/argv_to_request.dart)),
|
||||
so the C side stays a dumb pipe.
|
||||
|
||||
Build it with `make clide-cli` — output lands at
|
||||
`native/<platform>/clide` (gitignored). Drop that on your PATH (or
|
||||
symlink) and `clide status` works from any directory inside a clide
|
||||
workspace once the app is running. Standard POSIX + libc only;
|
||||
pure C99; no third-party deps.
|
||||
|
||||
The cross-language hash agreement is load-bearing — if the Dart
|
||||
server and C client disagree on the socket path, every shell
|
||||
invocation fails to connect. The test suite covers it:
|
||||
[`test/ipc/paths_test.dart`](test/ipc/paths_test.dart) pins
|
||||
FNV-1a vectors against the reference, and
|
||||
[`test/cli/clide_cli_e2e_test.dart`](test/cli/clide_cli_e2e_test.dart)
|
||||
compiles the C client and exercises the full round-trip.
|
||||
|
||||
## Running clide from the shell
|
||||
|
||||
Once the desktop app is running and `clide` is on your `$PATH`, every
|
||||
shell inside the workspace can drive it. The argv grammar is fixed by
|
||||
[D-6](governance/decisions/architecture.md#d-6-cli-and-event-surface-contract)
|
||||
— every UI affordance has a CLI counterpart, and every CLI verb has a
|
||||
UI affordance.
|
||||
|
||||
```sh
|
||||
clide status # one-shot snapshot of pane state
|
||||
clide ping # round-trip health check
|
||||
clide tail --events # stream every subsystem event
|
||||
clide tail --events --filter git # stream a single subsystem
|
||||
clide pane focus editor # drive the running app
|
||||
clide panel toggle git # show/hide a panel
|
||||
clide panel resize sidebar --to 320 # absolute width (px)
|
||||
clide panel resize context --by -40 # relative nudge (px)
|
||||
clide panel resize editor --to 0.5 # editor split ratio (0.15–0.70)
|
||||
```
|
||||
|
||||
Output is JSON on stdout, one envelope per line. Streaming verbs
|
||||
(`tail --events`) keep the connection open and emit one event line per
|
||||
push; Ctrl-C cleanly closes the socket. Exit codes follow the pql
|
||||
convention ([D-68](governance/decisions/architecture.md#d-68-cli-and-mcp-surface-contracts)):
|
||||
|
||||
| Code | Meaning |
|
||||
|------|------------------------------------------------------|
|
||||
| 0 | success |
|
||||
| 64 | user error — bad argv, unknown verb, malformed flag |
|
||||
| 65 | data error — request well-formed but rejected |
|
||||
| 69 | service unavailable — no running clide for this repo |
|
||||
| 70 | internal error — dispatcher threw |
|
||||
|
||||
If `clide` exits 69, the desktop app isn't running for this workspace
|
||||
— start it with `make run` (during development) or launch the
|
||||
installed app pointed at this repo.
|
||||
|
||||
### Claude Code's `/ide` and the MCP server
|
||||
|
||||
The desktop app also runs an MCP companion server on a random
|
||||
localhost port (HTTP + Server-Sent Events, per
|
||||
[D-73](governance/decisions/architecture.md#d-73-mcp-server-transport-over-http-sse)).
|
||||
Discovery follows the Claude Code `/ide` contract: clide writes a
|
||||
JSON descriptor to `~/.claude/ide/<pid>.lock` containing the chosen
|
||||
port, the workspace root, and the protocol version. Claude Code's
|
||||
`/ide` command picks the lock for the workspace it's running in and
|
||||
connects automatically — no manual configuration. The lock is removed
|
||||
on graceful shutdown; stale locks from crashed processes are reaped
|
||||
on the next start. The implementation lives in
|
||||
[`lib/src/ipc/mcp_server.dart`](lib/src/ipc/mcp_server.dart).
|
||||
|
||||
## Decisions, questions, rejected (DQR)
|
||||
|
||||
clide tracks architectural commitments as durable records under
|
||||
|
||||
@@ -19,6 +19,10 @@ else
|
||||
endif
|
||||
|
||||
VERSION ?= $(shell awk -F': *' '/^version:/ {gsub(/[" ]/,"",$$2); print $$2; exit}' pubspec.yaml)
|
||||
NAME ?= $(shell awk -F': *' '/^name:/ {gsub(/[" ]/,"",$$2); print $$2; exit}' pubspec.yaml)
|
||||
TAGLINE ?= $(shell awk -F': *' '/^tagline:/ {sub(/^tagline: *"?/,"",$$0); sub(/"$$/,"",$$0); print; exit}' pubspec.yaml)
|
||||
REPOSITORY ?= $(shell awk -F': *' '/^repository:/ {sub(/^repository: */,"",$$0); print; exit}' pubspec.yaml)
|
||||
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
.PHONY: help
|
||||
@@ -28,11 +32,14 @@ help: ## Show this help.
|
||||
# -- app (Flutter) -------------------------------------------------------
|
||||
|
||||
.PHONY: run
|
||||
run: ## Launch the Flutter desktop app.
|
||||
run: gen-build-info clide-cli ## Launch the Flutter desktop app.
|
||||
# CLIDE_CLI_BIN points the in-app "Install clide command in PATH"
|
||||
# affordance (T-212) at the dev-tree C client; a packaged build finds it
|
||||
# beside the GUI runner in the bundle instead.
|
||||
ifeq ($(FLUTTER_OS),linux)
|
||||
GDK_BACKEND=x11 LD_LIBRARY_PATH=$(CURDIR)/native/linux-x64$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} flutter run -d linux --dart-define=CLIDE_PROJECT=$(CURDIR)
|
||||
CLIDE_CLI_BIN=$(CURDIR)/$(CLIDE_CLI_BIN) GDK_BACKEND=x11 LD_LIBRARY_PATH=$(CURDIR)/native/linux-x64$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} flutter run -d linux --dart-define=CLIDE_PROJECT=$(CURDIR)
|
||||
else
|
||||
flutter run -d $(FLUTTER_OS) --dart-define=CLIDE_PROJECT=$(CURDIR)
|
||||
CLIDE_CLI_BIN=$(CURDIR)/$(CLIDE_CLI_BIN) flutter run -d $(FLUTTER_OS) --dart-define=CLIDE_PROJECT=$(CURDIR)
|
||||
endif
|
||||
|
||||
TESTMODE_CATEGORY ?= all
|
||||
@@ -64,7 +71,7 @@ pubget: ## flutter pub get.
|
||||
flutter pub get
|
||||
|
||||
.PHONY: build-check
|
||||
build-check: ## Verify native + Dart build compiles (no run).
|
||||
build-check: gen-build-info ## Verify native + Dart build compiles (no run).
|
||||
ifeq ($(FLUTTER_OS),linux)
|
||||
LD_LIBRARY_PATH=$(CURDIR)/native/linux-x64$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} flutter build linux
|
||||
else
|
||||
@@ -79,23 +86,50 @@ analyze: ## flutter analyze.
|
||||
format: ## dart format --set-exit-if-changed.
|
||||
dart format --set-exit-if-changed .
|
||||
|
||||
# The single bake of every build-time fact derived from pubspec.yaml
|
||||
# + git + clock. Runs implicitly as a prereq of every target that
|
||||
# compiles or executes Dart code so nobody has to remember it.
|
||||
#
|
||||
# Outputs:
|
||||
# - lib/src/build_info.g.dart — gitignored, fresh on every build.
|
||||
# Exposes `clideName`, `clideTagline`, `clideVersion`, `clideRepository`
|
||||
# (from pubspec), `clideCommit` (git short SHA), `clideDate` (UTC
|
||||
# now) for the app to read directly.
|
||||
# - assets/licenses.yaml `self.version:` — rewritten in place so the
|
||||
# bundled license manifest never drifts from pubspec. (Tracked in
|
||||
# git; the rewrite is a no-op when in sync.)
|
||||
.PHONY: gen-build-info
|
||||
gen-build-info:
|
||||
@printf '// GENERATED — do not edit. Regenerated by `make` on every\n// build/run/test (see gen-build-info in Makefile). Name / tagline /\n// version / repository come from pubspec.yaml — the single source\n// of truth. Commit + date stamp at run time.\nconst String clideName = '"'"'%s'"'"';\nconst String clideTagline = '"'"'%s'"'"';\nconst String clideVersion = '"'"'%s'"'"';\nconst String clideRepository = '"'"'%s'"'"';\nconst String clideCommit = '"'"'%s'"'"';\nconst String clideDate = '"'"'%s'"'"';\n' "$(NAME)" "$(TAGLINE)" "$(VERSION)" "$(REPOSITORY)" "$(COMMIT)" "$(DATE)" > lib/src/build_info.g.dart
|
||||
@awk -v v="$(VERSION)" '/^self:/ {in_self=1} in_self && /^[[:space:]]+version:/ {sub(/version:.*/, "version: \"" v "\""); in_self=0} {print}' assets/licenses.yaml > assets/licenses.yaml.tmp && mv assets/licenses.yaml.tmp assets/licenses.yaml
|
||||
|
||||
.PHONY: verify
|
||||
verify: analyze format decisions-validate changelog-gate ## No-tests sweep — analyze + format + decisions-validate + changelog-gate. For mid-edit "are the gates green?" checks; `push-check` is the full pre-push pipeline.
|
||||
verify: gen-build-info analyze format decisions-validate changelog-gate ## No-tests sweep — gen-build-info + analyze + format + decisions-validate + changelog-gate. For mid-edit "are the gates green?" checks; `push-check` is the full pre-push pipeline.
|
||||
|
||||
.PHONY: t
|
||||
t: gen-build-info ## Run one test path with tee'd output. Usage: make t T=test/path/to/spec.dart
|
||||
@mkdir -p test/.test-output
|
||||
@if [ -z "$(T)" ]; then echo "usage: make t T=test/path/to/spec.dart" >&2; exit 2; fi
|
||||
flutter test $(T) 2>&1 | tee test/.test-output/last.log
|
||||
|
||||
.PHONY: test
|
||||
test: ## Fast: analyze + format + unit + widget + golden (<60s).
|
||||
test: gen-build-info ## Fast dev loop: analyze + format + unit + widget + golden, NO coverage, parallel (~20s).
|
||||
ci/test.sh
|
||||
|
||||
.PHONY: test-coverage
|
||||
test-coverage: gen-build-info ## Same suite WITH coverage → coverage/lcov.info (for the gate / CI). Slower.
|
||||
ci/test.sh --coverage
|
||||
|
||||
.PHONY: test-core
|
||||
test-core: ## Core subsystem tests (IPC, PTY, git, pane registry).
|
||||
test-core: gen-build-info ## Core subsystem tests (IPC, PTY, git, pane registry).
|
||||
ci/test_core.sh
|
||||
|
||||
.PHONY: test-a11y
|
||||
test-a11y: ## A11y contract (semantic coverage + keyboard + contrast + i18n).
|
||||
test-a11y: gen-build-info ## A11y contract (semantic coverage + keyboard + contrast + i18n).
|
||||
ci/test_a11y.sh
|
||||
|
||||
.PHONY: test-integration
|
||||
test-integration: ## Integration tests (real app boot; xvfb on headless Linux).
|
||||
test-integration: gen-build-info ## Integration tests (real app boot; xvfb on headless Linux).
|
||||
ci/test_integration.sh
|
||||
|
||||
.PHONY: test-e2e
|
||||
@@ -106,7 +140,7 @@ test-e2e: ## End-to-end Playwright smoke.
|
||||
test-all: test-core test test-a11y test-integration test-e2e ## Everything, sequentially.
|
||||
|
||||
.PHONY: coverage-gate
|
||||
coverage-gate: ## Coverage gate — fails if total line % < pubspec.yaml `coverage_floor:` (D-66). Assumes `make test` ran first.
|
||||
coverage-gate: ## Coverage gate — fails if total line % < pubspec.yaml `coverage_floor:` (D-66). Assumes `make test-coverage` ran first.
|
||||
ci/coverage_gate.sh
|
||||
|
||||
.PHONY: changelog-gate
|
||||
@@ -135,15 +169,17 @@ ui-smoke: ## Build + serve + run Playwright smoke + stop.
|
||||
@sh -c 'trap "tools/ui/stop.sh >/dev/null 2>&1" EXIT; cd tools/ui && npx playwright test smoke.spec.ts'
|
||||
|
||||
.PHONY: build
|
||||
build: ## flutter build for the current OS.
|
||||
build: gen-build-info clide-cli ## flutter build for the current OS (incl. the C CLI client).
|
||||
flutter build $(FLUTTER_OS)
|
||||
@install -m 755 $(CLIDE_CLI_BIN) $(CLI_BUNDLE_DEST)
|
||||
@echo "==> bundled C client at $(CLI_BUNDLE_DEST)"
|
||||
|
||||
.PHONY: build-linux
|
||||
build-linux: ## flutter build linux (desktop bundle).
|
||||
build-linux: gen-build-info ## flutter build linux (desktop bundle).
|
||||
flutter build linux
|
||||
|
||||
.PHONY: build-macos
|
||||
build-macos: ## flutter build macos (desktop bundle).
|
||||
build-macos: gen-build-info ## flutter build macos (desktop bundle).
|
||||
flutter build macos
|
||||
|
||||
# -- install / uninstall -----------------------------------------------------
|
||||
@@ -154,19 +190,23 @@ INSTALL_PREFIX ?= $(HOME)/.local/lib
|
||||
|
||||
ifeq ($(FLUTTER_OS),linux)
|
||||
BUNDLE_DIR := build/linux/x64/release/bundle
|
||||
# The C client ships beside the GUI runner so the in-app "Install clide
|
||||
# command in PATH" affordance (T-212) can self-install from the bundle.
|
||||
CLI_BUNDLE_DEST := $(BUNDLE_DIR)/clide-cli
|
||||
else ifeq ($(FLUTTER_OS),macos)
|
||||
BUNDLE_DIR := build/macos/Build/Products/Release/clide.app
|
||||
CLI_BUNDLE_DEST := $(BUNDLE_DIR)/Contents/MacOS/clide-cli
|
||||
endif
|
||||
|
||||
ICON_SIZES := 16 32 48 128 192 256 512
|
||||
|
||||
.PHONY: install
|
||||
install: build ## Build + install clide to ~/.local (INSTALL_PREFIX, INSTALL_DIR).
|
||||
install: build clide-cli ## Build + install clide to ~/.local (INSTALL_PREFIX, INSTALL_DIR).
|
||||
ifeq ($(FLUTTER_OS),linux)
|
||||
@mkdir -p $(INSTALL_PREFIX) $(INSTALL_DIR)
|
||||
rm -rf $(INSTALL_PREFIX)/clide
|
||||
cp -a $(BUNDLE_DIR) $(INSTALL_PREFIX)/clide
|
||||
ln -sf $(INSTALL_PREFIX)/clide/clide $(INSTALL_DIR)/clide
|
||||
install -m 755 $(CLIDE_CLI_BIN) $(INSTALL_DIR)/clide
|
||||
@for size in $(ICON_SIZES); do \
|
||||
dir=$(HOME)/.local/share/icons/hicolor/$${size}x$${size}/apps; \
|
||||
mkdir -p $$dir; \
|
||||
@@ -177,14 +217,17 @@ ifeq ($(FLUTTER_OS),linux)
|
||||
> $(HOME)/.local/share/applications/net.schweitz.clide.desktop
|
||||
@gtk-update-icon-cache -f -t $(HOME)/.local/share/icons/hicolor 2>/dev/null || true
|
||||
@update-desktop-database $(HOME)/.local/share/applications 2>/dev/null || true
|
||||
@echo "installed: $(INSTALL_DIR)/clide -> $(INSTALL_PREFIX)/clide/clide"
|
||||
@echo "cli: $(INSTALL_DIR)/clide (C client)"
|
||||
@echo "gui: $(INSTALL_PREFIX)/clide/clide"
|
||||
@echo "desktop: ~/.local/share/applications/net.schweitz.clide.desktop"
|
||||
@echo "version: $(VERSION)"
|
||||
else ifeq ($(FLUTTER_OS),macos)
|
||||
@mkdir -p $(HOME)/Applications
|
||||
@mkdir -p $(HOME)/Applications $(INSTALL_DIR)
|
||||
rm -rf $(HOME)/Applications/clide.app
|
||||
cp -a $(BUNDLE_DIR) $(HOME)/Applications/clide.app
|
||||
@echo "installed: ~/Applications/clide.app"
|
||||
install -m 755 $(CLIDE_CLI_BIN) $(INSTALL_DIR)/clide
|
||||
@echo "gui: ~/Applications/clide.app"
|
||||
@echo "cli: $(INSTALL_DIR)/clide (C client)"
|
||||
@echo "version: $(VERSION)"
|
||||
else
|
||||
@echo "install not yet supported on $(FLUTTER_OS)"
|
||||
@@ -205,6 +248,7 @@ ifeq ($(FLUTTER_OS),linux)
|
||||
@update-desktop-database $(HOME)/.local/share/applications 2>/dev/null || true
|
||||
@echo "uninstalled"
|
||||
else ifeq ($(FLUTTER_OS),macos)
|
||||
rm -f $(INSTALL_DIR)/clide
|
||||
rm -rf $(HOME)/Applications/clide.app
|
||||
@echo "uninstalled"
|
||||
endif
|
||||
@@ -241,11 +285,32 @@ dugite-fetch: ## Download and extract the dugite-native git distribution.
|
||||
dugite-clean: ## Remove the dugite-native directory.
|
||||
rm -rf $(DUGITE_DIR)
|
||||
|
||||
# -- clide-cli ----------------------------------------------------------
|
||||
# The C `clide` shell client that talks to the in-process IPC server
|
||||
# (T-99 / T-126). One source file, no third-party deps; the build
|
||||
# target picks up whatever `cc` is on PATH.
|
||||
|
||||
CLIDE_CLI_SRC := native/clide-cli/clide.c
|
||||
CLIDE_CLI_BIN := native/$(if $(filter Darwin,$(shell uname -s)),macos,linux)-$(shell uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/')/clide
|
||||
CC ?= cc
|
||||
|
||||
.PHONY: clide-cli
|
||||
clide-cli: $(CLIDE_CLI_BIN) ## Compile the C `clide` shell client.
|
||||
|
||||
$(CLIDE_CLI_BIN): $(CLIDE_CLI_SRC)
|
||||
@mkdir -p $(dir $(CLIDE_CLI_BIN))
|
||||
$(CC) -std=c99 -O2 -Wall -Wextra -o $(CLIDE_CLI_BIN) $(CLIDE_CLI_SRC)
|
||||
@echo "==> built $(CLIDE_CLI_BIN)"
|
||||
|
||||
.PHONY: clide-cli-clean
|
||||
clide-cli-clean: ## Remove the compiled C `clide` client.
|
||||
rm -f $(CLIDE_CLI_BIN)
|
||||
|
||||
# -- security -------------------------------------------------------------
|
||||
|
||||
.PHONY: security
|
||||
security: ## Dart advisory review.
|
||||
@echo "security: Dart advisories reviewed manually before pubspec.yaml bumps."
|
||||
security: ## Supply-chain gate — osv-scanner over pubspec.lock (CI PR-merge pipeline; run locally on demand). Fails on a known advisory.
|
||||
ci/osv_scan.sh
|
||||
|
||||
# -- pre-push gate --------------------------------------------------------
|
||||
|
||||
@@ -254,10 +319,14 @@ decisions-validate: ## Parser dry-run over governance/{decisions,questions,rejec
|
||||
pql decisions validate
|
||||
|
||||
.PHONY: push-check
|
||||
push-check: decisions-validate test-core test test-a11y coverage-gate changelog-gate ## Pre-push gate (fast — <2 min target).
|
||||
# NOTE: the `security` (osv-scanner) gate is deliberately NOT in push-check —
|
||||
# it runs in the CI PR-merge pipeline (where the scanner is provisioned) so we
|
||||
# don't force every dev machine to install osv-scanner. Run it locally any time
|
||||
# with `make security`.
|
||||
push-check: decisions-validate changelog-gate test-coverage coverage-gate test-core ## Pre-push gate (fast — <2 min target). Order is fail-fast: instant gates (decisions, changelog) first, then the coverage suite + gate (the expensive, most-likely-to-fail stage) BEFORE test-core — a coverage miss aborts here instead of after running everything, so a fix doesn't force a full re-run of the rest. test-coverage already runs the a11y suite (test/a11y), so no separate test-a11y pass.
|
||||
|
||||
.PHONY: push-check-full
|
||||
push-check-full: push-check test-integration smoke-bundle ## Pre-release gate (push-check + integration + smoke; slower; skips theme_picker per T-116).
|
||||
push-check-full: push-check test-integration smoke-bundle ## Pre-release gate (push-check + integration + smoke; slower).
|
||||
|
||||
.PHONY: hooks
|
||||
hooks: ## Install the repo's git hooks.
|
||||
|
||||
@@ -19,10 +19,14 @@ canvas, claude, claude_control, decisions, diff, editor, extensions_ui, files, g
|
||||
|
||||
## Building
|
||||
|
||||
Requires Flutter (stable channel) on the host. One-time setup:
|
||||
Requires Flutter (stable channel) and [pql](https://github.com/postmeridiem/pql)
|
||||
on the host. pql is a hard dependency — the pre-push gate runs `pql decisions
|
||||
validate` and the governance + ticket workflow is built on it (see its repo for
|
||||
install). One-time setup:
|
||||
|
||||
```
|
||||
make hooks && flutter pub get
|
||||
pql init # once, in the repo root — wires up the pql skill + perms
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
@@ -53,6 +53,33 @@ bindings:
|
||||
keys: escape
|
||||
when: palette.open
|
||||
|
||||
# -- Quick open (fuzzy file finder) -----------------------------------
|
||||
# ctrl+p / meta+p are free while the palette is closed; the palette
|
||||
# only claims ctrl+p for selectPrevious `when: palette.open`, so this
|
||||
# is conflict-free. Arrow/enter/escape inside the overlay are handled
|
||||
# locally by the widget.
|
||||
- intent: quickOpen.open
|
||||
keys: [ctrl+p, meta+p]
|
||||
when: "!palette.open"
|
||||
# Nav stays on arrows/ctrl+n (not ctrl+p — that's the open chord and
|
||||
# would collide while the overlay is up).
|
||||
- intent: quickOpen.selectNext
|
||||
keys: [down, ctrl+n]
|
||||
when: quickOpen.open
|
||||
- intent: quickOpen.selectPrevious
|
||||
keys: up
|
||||
when: quickOpen.open
|
||||
- intent: quickOpen.accept
|
||||
keys: enter
|
||||
when: quickOpen.open
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
when: quickOpen.open
|
||||
|
||||
# -- Find in files ----------------------------------------------------
|
||||
- intent: findInFiles.open
|
||||
keys: [ctrl+shift+f, meta+shift+f]
|
||||
|
||||
# -- Text scale -------------------------------------------------------
|
||||
# On most layouts `+` is `shift+equal`; we bind both so users who
|
||||
# think of it as Ctrl+Plus and users who hit Ctrl+= both work.
|
||||
@@ -62,3 +89,9 @@ bindings:
|
||||
keys: [ctrl+minus, meta+minus]
|
||||
- intent: text.scaleReset
|
||||
keys: [ctrl+0, meta+0]
|
||||
|
||||
# -- File (application menu, T-48) -------------------------------------
|
||||
- intent: command:file.openFolder
|
||||
keys: [ctrl+o, meta+o]
|
||||
- intent: command:file.newWindow
|
||||
keys: [ctrl+shift+n, meta+shift+n]
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# clide JetBrains / IntelliJ keymap preset (T-66).
|
||||
#
|
||||
# Maps IntelliJ's default keybindings to clide intents + commands. A preset
|
||||
# fully REPLACES the active layer, so this file is self-contained.
|
||||
#
|
||||
# Notation (D-82): `+` joins a chord, a space sequences, a YAML list
|
||||
# alternates. IntelliJ's mac and Linux/Windows defaults diverge for several
|
||||
# actions (Go to File is Cmd+Shift+O on mac but Ctrl+Shift+N on win/linux),
|
||||
# so both spellings are bound.
|
||||
#
|
||||
# Scope flags: only `palette.open` / `quickOpen.open` are published today;
|
||||
# IntelliJ's editor-scoped contexts have no producer yet, so global chords
|
||||
# stay ungated (they're global in IntelliJ too).
|
||||
#
|
||||
# Not bound — no clide command analogue (kept out of scope per the ticket):
|
||||
# Run (Shift+F10), Debug (Shift+F9), Rename/Refactor (Shift+F6), Settings
|
||||
# (Ctrl+Alt+S). And "Search Everywhere" (double-Shift) is not expressible by
|
||||
# the current chord matcher (bare/double modifiers unsupported) — tracked by
|
||||
# T-341; Go to File / Find Action below are the practical stand-ins.
|
||||
|
||||
name: jetbrains
|
||||
|
||||
bindings:
|
||||
# -- Activation / focus -----------------------------------------------
|
||||
# No F6 panel cycling here: IntelliJ uses F6 (Move) / Shift+F6 (Rename),
|
||||
# so binding them to panel focus would fight muscle memory.
|
||||
- intent: activate
|
||||
keys: [enter, space]
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
- intent: focus.next
|
||||
keys: tab
|
||||
- intent: focus.previous
|
||||
keys: shift+tab
|
||||
|
||||
# -- Find Action (≈ command palette): Ctrl+Shift+A --------------------
|
||||
- intent: palette.open
|
||||
keys: [ctrl+shift+a, meta+shift+a]
|
||||
- intent: palette.selectNext
|
||||
keys: down
|
||||
when: palette.open
|
||||
- intent: palette.selectPrevious
|
||||
keys: up
|
||||
when: palette.open
|
||||
- intent: palette.accept
|
||||
keys: enter
|
||||
when: palette.open
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
when: palette.open
|
||||
|
||||
# -- Quick open: Go to File / Go to Class / Recent Files --------------
|
||||
# win/linux: Ctrl+Shift+N, Ctrl+N, Ctrl+E. mac: Cmd+Shift+O, Cmd+O,
|
||||
# Cmd+E. clide has one fuzzy file finder, so all land on quick-open.
|
||||
- intent: quickOpen.open
|
||||
keys: [ctrl+shift+n, ctrl+n, ctrl+e, meta+shift+o, meta+o, meta+e]
|
||||
when: "!palette.open"
|
||||
- intent: quickOpen.selectNext
|
||||
keys: down
|
||||
when: quickOpen.open
|
||||
- intent: quickOpen.selectPrevious
|
||||
keys: up
|
||||
when: quickOpen.open
|
||||
- intent: quickOpen.accept
|
||||
keys: enter
|
||||
when: quickOpen.open
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
when: quickOpen.open
|
||||
|
||||
# -- Find in Path (search): Ctrl+Shift+F ------------------------------
|
||||
- intent: findInFiles.open
|
||||
keys: [ctrl+shift+f, meta+shift+f]
|
||||
|
||||
# -- Tool windows -----------------------------------------------------
|
||||
# Project (Alt+1) → sidebar; Terminal (Alt+F12) → bottom dock; Hide All
|
||||
# Windows / distraction-free (Ctrl+Shift+F12) → focus (zen) mode.
|
||||
- intent: command:sidebar.collapse
|
||||
keys: [alt+1, meta+1]
|
||||
- intent: command:dock.toggle
|
||||
keys: alt+f12
|
||||
- intent: command:panel.focusMode
|
||||
keys: [ctrl+shift+f12, meta+shift+f12]
|
||||
|
||||
# -- Editor -----------------------------------------------------------
|
||||
# Close active tab: Ctrl+F4 (win/linux) / Cmd+W (mac).
|
||||
- intent: command:editor.close
|
||||
keys: [ctrl+f4, meta+w]
|
||||
# Zoom is a clide convenience — IntelliJ has no default zoom keys.
|
||||
- intent: command:view.zoomIn
|
||||
keys: [ctrl+equal, ctrl+shift+equal, meta+equal, meta+shift+equal]
|
||||
- intent: command:view.zoomOut
|
||||
keys: [ctrl+minus, meta+minus]
|
||||
- intent: command:view.zoomReset
|
||||
keys: [ctrl+0, meta+0]
|
||||
@@ -0,0 +1,169 @@
|
||||
# clide Vim keymap preset (T-65).
|
||||
#
|
||||
# Modal editing built on the keymap sequence layer (D-82). The Vim mode
|
||||
# service (builtin.vim) publishes vim.normal / vim.insert / vim.visual
|
||||
# scope flags; bindings here are guarded on them. Motions/edits resolve to
|
||||
# `command:editor.vim.<action>`, which the editor applies to the buffer
|
||||
# (read-only in command mode so keys never type); mode changes resolve to
|
||||
# `command:vim.mode.<mode>`.
|
||||
#
|
||||
# Notation (D-82): `+` joins a chord, a space sequences (`d d`), a YAML
|
||||
# list alternates (`[d, x]`). Repeat counts (`5j`) are captured by the
|
||||
# matcher — no binding needed.
|
||||
#
|
||||
# Scope: the muscle-memory set, not bit-exact Vim. cw/$/^ are
|
||||
# approximations; see vim_edit_ops.dart.
|
||||
|
||||
name: vim
|
||||
|
||||
bindings:
|
||||
# ---- App shortcuts (shared with the default preset) -----------------
|
||||
# Modified chords bubble past the editor to the global handler, so these
|
||||
# keep working with the editor focused and under every mode.
|
||||
- intent: palette.open
|
||||
keys: [ctrl+shift+p, meta+shift+p]
|
||||
- intent: palette.selectNext
|
||||
keys: [down, ctrl+n]
|
||||
when: palette.open
|
||||
- intent: palette.selectPrevious
|
||||
keys: [up, ctrl+p]
|
||||
when: palette.open
|
||||
- intent: palette.accept
|
||||
keys: enter
|
||||
when: palette.open
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
when: palette.open
|
||||
- intent: quickOpen.open
|
||||
keys: [ctrl+p, meta+p]
|
||||
when: "!palette.open"
|
||||
- intent: quickOpen.selectNext
|
||||
keys: [down, ctrl+n]
|
||||
when: quickOpen.open
|
||||
- intent: quickOpen.selectPrevious
|
||||
keys: up
|
||||
when: quickOpen.open
|
||||
- intent: quickOpen.accept
|
||||
keys: enter
|
||||
when: quickOpen.open
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
when: quickOpen.open
|
||||
- intent: findInFiles.open
|
||||
keys: [ctrl+shift+f, meta+shift+f]
|
||||
- intent: focus.nextPanel
|
||||
keys: f6
|
||||
- intent: focus.previousPanel
|
||||
keys: shift+f6
|
||||
- intent: text.scaleIncrease
|
||||
keys: [ctrl+equal, ctrl+shift+equal, meta+equal, meta+shift+equal]
|
||||
- intent: text.scaleDecrease
|
||||
keys: [ctrl+minus, meta+minus]
|
||||
- intent: text.scaleReset
|
||||
keys: [ctrl+0, meta+0]
|
||||
|
||||
# ---- Mode transitions ------------------------------------------------
|
||||
- intent: command:vim.mode.visual
|
||||
keys: v
|
||||
when: vim.normal
|
||||
- intent: command:vim.mode.normal
|
||||
keys: escape
|
||||
when: "vim.insert || vim.visual"
|
||||
|
||||
# ---- Insert entry (normal → insert), positioned by the editor --------
|
||||
- intent: command:editor.vim.insert
|
||||
keys: i
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.append
|
||||
keys: a
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.insertLineStart
|
||||
keys: shift+i # I
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.appendLineEnd
|
||||
keys: shift+a # A
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.openBelow
|
||||
keys: o
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.openAbove
|
||||
keys: shift+o # O
|
||||
when: vim.normal
|
||||
|
||||
# ---- Motions (normal + visual) ---------------------------------------
|
||||
- intent: command:editor.vim.left
|
||||
keys: h
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.down
|
||||
keys: j
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.up
|
||||
keys: k
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.right
|
||||
keys: l
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.wordForward
|
||||
keys: w
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.wordBackward
|
||||
keys: b
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.wordEnd
|
||||
keys: e
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.lineStart
|
||||
keys: "0"
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.lineEnd
|
||||
keys: shift+4 # $
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.firstNonBlank
|
||||
keys: shift+6 # ^
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.docStart
|
||||
keys: "g g" # gg
|
||||
when: "vim.normal || vim.visual"
|
||||
- intent: command:editor.vim.docEnd
|
||||
keys: shift+g # G
|
||||
when: "vim.normal || vim.visual"
|
||||
|
||||
# ---- Normal-mode edits ----------------------------------------------
|
||||
- intent: command:editor.vim.deleteChar
|
||||
keys: x
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.deleteLine
|
||||
keys: "d d"
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.deleteWord
|
||||
keys: "d w"
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.deleteToEnd
|
||||
keys: shift+d # D
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.changeLine
|
||||
keys: "c c"
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.changeWord
|
||||
keys: "c w"
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.yankLine
|
||||
keys: "y y"
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.paste
|
||||
keys: p
|
||||
when: vim.normal
|
||||
- intent: command:editor.vim.pasteBefore
|
||||
keys: shift+p # P
|
||||
when: vim.normal
|
||||
|
||||
# ---- Visual-mode operators ------------------------------------------
|
||||
- intent: command:editor.vim.visualDelete
|
||||
keys: [d, x]
|
||||
when: vim.visual
|
||||
- intent: command:editor.vim.visualYank
|
||||
keys: y
|
||||
when: vim.visual
|
||||
- intent: command:editor.vim.visualChange
|
||||
keys: c
|
||||
when: vim.visual
|
||||
@@ -0,0 +1,114 @@
|
||||
# clide VS Code keymap preset (T-64).
|
||||
#
|
||||
# Maps VS Code's default keybindings to clide intents + commands so users
|
||||
# with VS Code muscle memory feel at home. A preset fully REPLACES the
|
||||
# active layer, so this file is self-contained (activation, focus, palette
|
||||
# and quick-open navigation are all repeated from `default.yaml`).
|
||||
#
|
||||
# Notation (D-82): `+` joins a chord, a space sequences (`ctrl+k z`), a
|
||||
# YAML list alternates (`[ctrl+b, meta+b]`). Both Ctrl (Linux/Windows) and
|
||||
# Meta/Cmd (macOS) variants are bound so one preset serves every platform.
|
||||
#
|
||||
# Scope flags: only `palette.open` and `quickOpen.open` are published today
|
||||
# (by the palette / quick-open overlays). VS Code's editor-scoped contexts
|
||||
# (`editor.focused`, `inputFocused`) have no producer yet, so the global
|
||||
# chords below stay ungated — which matches VS Code, where these commands
|
||||
# (palette, quick-open, sidebar toggle, …) are global anyway. Editor-text-
|
||||
# scoped gating lands when those scope producers do.
|
||||
|
||||
name: vscode
|
||||
|
||||
bindings:
|
||||
# -- Activation / focus -----------------------------------------------
|
||||
- intent: activate
|
||||
keys: [enter, space]
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
- intent: focus.next
|
||||
keys: tab
|
||||
- intent: focus.previous
|
||||
keys: shift+tab
|
||||
# Cycle the editor "parts" / panels (VS Code F6).
|
||||
- intent: focus.nextPanel
|
||||
keys: f6
|
||||
- intent: focus.previousPanel
|
||||
keys: shift+f6
|
||||
|
||||
# -- Command palette (Ctrl+Shift+P, F1) -------------------------------
|
||||
- intent: palette.open
|
||||
keys: [ctrl+shift+p, meta+shift+p, f1]
|
||||
- intent: palette.selectNext
|
||||
keys: [down, ctrl+n]
|
||||
when: palette.open
|
||||
- intent: palette.selectPrevious
|
||||
keys: [up, ctrl+p]
|
||||
when: palette.open
|
||||
- intent: palette.accept
|
||||
keys: enter
|
||||
when: palette.open
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
when: palette.open
|
||||
|
||||
# -- Quick open / Go to File (Ctrl+P) ---------------------------------
|
||||
- intent: quickOpen.open
|
||||
keys: [ctrl+p, meta+p]
|
||||
when: "!palette.open"
|
||||
- intent: quickOpen.selectNext
|
||||
keys: [down, ctrl+n]
|
||||
when: quickOpen.open
|
||||
- intent: quickOpen.selectPrevious
|
||||
keys: up
|
||||
when: quickOpen.open
|
||||
- intent: quickOpen.accept
|
||||
keys: enter
|
||||
when: quickOpen.open
|
||||
- intent: dismiss
|
||||
keys: escape
|
||||
when: quickOpen.open
|
||||
|
||||
# -- Search (Ctrl+Shift+F) --------------------------------------------
|
||||
- intent: findInFiles.open
|
||||
keys: [ctrl+shift+f, meta+shift+f]
|
||||
|
||||
# -- Panel toggles ----------------------------------------------------
|
||||
# Toggle Side Bar (Ctrl+B), Panel (Ctrl+J), Secondary Side Bar
|
||||
# (Ctrl+Alt+B). clide's left sidebar / bottom dock / right context bar.
|
||||
- intent: command:sidebar.collapse
|
||||
keys: [ctrl+b, meta+b]
|
||||
- intent: command:dock.toggle
|
||||
keys: [ctrl+j, meta+j]
|
||||
- intent: command:context.collapse
|
||||
keys: [ctrl+alt+b, meta+alt+b]
|
||||
# Toggle Terminal (Ctrl+`). clide has one bottom dock where shell/output
|
||||
# live, so the terminal chord targets the same dock as Ctrl+J.
|
||||
- intent: command:dock.toggle
|
||||
keys: [ctrl+backquote, meta+backquote]
|
||||
# Zen Mode (Ctrl+K Z).
|
||||
- intent: command:panel.focusMode
|
||||
keys: ["ctrl+k z", "meta+k z"]
|
||||
|
||||
# -- Editor actions ---------------------------------------------------
|
||||
# Close Editor (Ctrl+W).
|
||||
- intent: command:editor.close
|
||||
keys: [ctrl+w, meta+w]
|
||||
# Window zoom (VS Code View: Zoom In/Out/Reset). `+` is shift+equal on
|
||||
# most layouts, so bind both equal and shift+equal.
|
||||
- intent: command:view.zoomIn
|
||||
keys: [ctrl+equal, ctrl+shift+equal, meta+equal, meta+shift+equal]
|
||||
- intent: command:view.zoomOut
|
||||
keys: [ctrl+minus, meta+minus]
|
||||
- intent: command:view.zoomReset
|
||||
keys: [ctrl+0, meta+0]
|
||||
|
||||
# -- File / workspace -------------------------------------------------
|
||||
# Open Folder (Ctrl+K Ctrl+O), New Window (Ctrl+Shift+N), Close Folder
|
||||
# (Ctrl+K F), Color Theme (Ctrl+K Ctrl+T).
|
||||
- intent: command:file.openFolder
|
||||
keys: ["ctrl+k ctrl+o", "meta+k meta+o"]
|
||||
- intent: command:file.newWindow
|
||||
keys: [ctrl+shift+n, meta+shift+n]
|
||||
- intent: command:file.closeWorkspace
|
||||
keys: ["ctrl+k f", "meta+k f"]
|
||||
- intent: command:theme.pick
|
||||
keys: ["ctrl+k ctrl+t", "meta+k meta+t"]
|
||||
@@ -36,7 +36,10 @@ schema_version: 1
|
||||
# About screen can read it at runtime.
|
||||
self:
|
||||
name: clide
|
||||
version: "2.0.0-dev"
|
||||
# Auto-synced from pubspec.yaml `version:` by `make gen-build-info`
|
||||
# (runs implicitly on every build/run/test). Don't hand-edit; bump
|
||||
# pubspec instead.
|
||||
version: "2.3.3"
|
||||
homepage: https://github.com/postmeridiem/clide
|
||||
license: MIT
|
||||
license_file: assets/LICENSE
|
||||
@@ -78,6 +81,17 @@ dependencies:
|
||||
Fill weights bundled.
|
||||
weights_bundled: [Regular, Bold, Fill]
|
||||
|
||||
- name: Catppuccin (palette)
|
||||
kind: theme-palette
|
||||
version: "mocha"
|
||||
homepage: https://github.com/catppuccin/catppuccin
|
||||
license: MIT
|
||||
purpose: >-
|
||||
Source palette for the bundled catppuccin-mocha theme and its
|
||||
high-contrast sibling catppuccin-mocha-hc. Official hex values,
|
||||
mapped to clide's SurfaceTokens (Mauve accent, Blue primary); the
|
||||
faithful palette is not retuned (D-69).
|
||||
|
||||
- name: yaml
|
||||
kind: dart-package
|
||||
version: "3.1.3"
|
||||
@@ -102,7 +116,7 @@ dependencies:
|
||||
|
||||
- name: ffi
|
||||
kind: dart-package
|
||||
version: "2.1.3"
|
||||
version: "2.2.0"
|
||||
homepage: https://pub.dev/packages/ffi
|
||||
license: BSD-3-Clause
|
||||
purpose: >-
|
||||
@@ -130,7 +144,7 @@ dependencies:
|
||||
purpose: >-
|
||||
Incremental parsing library with embedded WASM grammar engine.
|
||||
Vendored as libtree-sitter.so (wasmtime statically linked) in
|
||||
app/native/. Called via dart:ffi. Loads grammar .wasm files
|
||||
native/linux-x64/. Called via dart:ffi. Loads grammar .wasm files
|
||||
through its built-in WASM store API.
|
||||
|
||||
- name: wasmtime
|
||||
@@ -157,7 +171,7 @@ dependencies:
|
||||
|
||||
- name: jovial_svg
|
||||
kind: dart-package
|
||||
version: "1.1.26"
|
||||
version: "1.1.30"
|
||||
homepage: https://pub.dev/packages/jovial_svg
|
||||
license: BSD-3-Clause
|
||||
purpose: >-
|
||||
@@ -168,7 +182,7 @@ dependencies:
|
||||
|
||||
- name: markdown
|
||||
kind: dart-package
|
||||
version: "7.2.2"
|
||||
version: "7.3.1"
|
||||
homepage: https://pub.dev/packages/markdown
|
||||
license: BSD-3-Clause
|
||||
purpose: >-
|
||||
@@ -193,7 +207,7 @@ dependencies:
|
||||
dev_dependencies:
|
||||
- name: mocktail
|
||||
kind: dart-package
|
||||
version: "1.0.4"
|
||||
version: "1.0.5"
|
||||
homepage: https://pub.dev/packages/mocktail
|
||||
license: MIT
|
||||
purpose: >-
|
||||
@@ -218,9 +232,25 @@ dev_dependencies:
|
||||
|
||||
- name: test
|
||||
kind: dart-package
|
||||
version: "1.30.0"
|
||||
version: "1.31.0"
|
||||
homepage: https://pub.dev/packages/test
|
||||
license: BSD-3-Clause
|
||||
purpose: >-
|
||||
Dart test runner for the Flutter-free PTY tests under
|
||||
`dart test --tags forkpty` (flutter_test is used elsewhere).
|
||||
`dart test --tags pty` (flutter_test is used elsewhere).
|
||||
|
||||
# Trademark / brand notices — third-party marks clide references but does
|
||||
# NOT bundle. No artefact ships for these; tracked here for audit
|
||||
# completeness and to record the nominative-use basis.
|
||||
trademark_notices:
|
||||
- mark: Claude name + accent colour (#d97757)
|
||||
owner: Anthropic, PBC
|
||||
reference: https://github.com/anthropics/skills/tree/main/skills/brand-guidelines
|
||||
usage: >-
|
||||
Nominative use only. "Claude" identifies the CLI that clide
|
||||
integrates with; #d97757 is Anthropic's published Claude accent
|
||||
colour, used solely to mark Claude's own messages/labels in the
|
||||
conversation UI (not as clide's brand colour). clide is not
|
||||
affiliated with, endorsed by, or partnered with Anthropic, bundles
|
||||
no Anthropic logo or brand artwork, and ships its own logo. CLAUDE
|
||||
is a registered trademark of Anthropic, PBC.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Merge lcov files into one, on stdout.
|
||||
|
||||
clide runs coverage in two passes — the parallel pool and a serial
|
||||
(`--tags serial --concurrency=1`) pass for tests that can't share the
|
||||
parallel runner (T-193). Each `flutter test --coverage` pass overwrites
|
||||
coverage/lcov.info, and the coverage gate naively sums LF:/LH: across
|
||||
records, so a source file appearing in BOTH passes would double-count.
|
||||
|
||||
This unions DA (line→hits) per source file, taking the MAX hit count (a
|
||||
line executed in EITHER pass counts as hit), then recomputes LF/LH. Line
|
||||
coverage only — which is all flutter emits and all the gate reads. No
|
||||
`lcov` dependency.
|
||||
|
||||
Usage: merge_lcov.py a.info b.info [...] > merged.info
|
||||
"""
|
||||
import sys
|
||||
|
||||
files = {} # source path -> {line: hits}
|
||||
order = [] # first-seen order, for stable output
|
||||
|
||||
for path in sys.argv[1:]:
|
||||
cur = None
|
||||
with open(path) as fh:
|
||||
for raw in fh:
|
||||
line = raw.rstrip("\n")
|
||||
if line.startswith("SF:"):
|
||||
cur = line[3:]
|
||||
if cur not in files:
|
||||
files[cur] = {}
|
||||
order.append(cur)
|
||||
elif line.startswith("DA:") and cur is not None:
|
||||
num, _, hits = line[3:].partition(",")
|
||||
num, hits = int(num), int(hits)
|
||||
files[cur][num] = max(files[cur].get(num, 0), hits)
|
||||
elif line == "end_of_record":
|
||||
cur = None
|
||||
|
||||
out = []
|
||||
for sf in order:
|
||||
da = files[sf]
|
||||
out.append("SF:" + sf)
|
||||
for num in sorted(da):
|
||||
out.append(f"DA:{num},{da[num]}")
|
||||
out.append(f"LF:{len(da)}")
|
||||
out.append(f"LH:{sum(1 for h in da.values() if h > 0)}")
|
||||
out.append("end_of_record")
|
||||
|
||||
sys.stdout.write("\n".join(out) + "\n")
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# Supply-chain gate — fails the push if any resolved dependency in
|
||||
# pubspec.lock has a known advisory (OSV / GitHub Advisory Database).
|
||||
#
|
||||
# Complements `dart pub get`'s passive advisory print (informational,
|
||||
# non-failing) with a hard, fail-closed gate. Native deps (dugite,
|
||||
# tree-sitter, wasmtime) are vendored by SHA and not in a lockfile OSV
|
||||
# reads — they're reviewed separately on bump (D-42, CLAUDE.md supply chain).
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Resolve osv-scanner: PATH first, then a brew prefix (the git pre-push hook
|
||||
# may run with a leaner PATH than the dev's interactive shell).
|
||||
OSV="$(command -v osv-scanner || true)"
|
||||
if [[ -z "$OSV" ]] && command -v brew >/dev/null 2>&1; then
|
||||
cand="$(brew --prefix 2>/dev/null)/bin/osv-scanner"
|
||||
[[ -x "$cand" ]] && OSV="$cand"
|
||||
fi
|
||||
if [[ -z "$OSV" ]]; then
|
||||
echo "==> osv gate: osv-scanner not found on PATH." >&2
|
||||
echo " Install it: brew install osv-scanner" >&2
|
||||
echo " (or: go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest)" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "==> osv gate: scanning pubspec.lock for known advisories"
|
||||
if "$OSV" scan source --lockfile=pubspec.lock; then
|
||||
echo "==> osv gate OK: no known advisories"
|
||||
else
|
||||
echo "==> osv gate FAIL: a dependency has a known advisory (see above)." >&2
|
||||
echo " Bump the affected package (+ its assets/licenses.yaml entry), or" >&2
|
||||
echo " document an explicit, justified exception before pushing." >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,18 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fast test layer — analyze + format + unit + widget + golden.
|
||||
# Runs in <60s on a warm cache. Called from `make test` and the
|
||||
# pre-push hook.
|
||||
#
|
||||
# Two modes (T-192):
|
||||
# ci/test.sh dev inner loop — NO coverage, parallel. ~20s warm.
|
||||
# ci/test.sh --coverage gate run — instrumented; writes coverage/lcov.info
|
||||
# for coverage-gate. ~36s (coverage is the floor and
|
||||
# is concurrency-insensitive, measured — so no
|
||||
# --concurrency here; it buys nothing).
|
||||
#
|
||||
# Both pass `--timeout 60s` so a hung test (a stray pumpAndSettle, or a
|
||||
# real-time deadlock) fails fast instead of wedging the runner for ~10 min and
|
||||
# stalling the pre-push gate. (Use the pumpAsync helper in tests; never
|
||||
# pumpAndSettle / Future.delayed(Duration.zero) inside testWidgets.)
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Reporter: failures-only keeps the gate output to failing tests + a final
|
||||
# pass/fail count, instead of one line per test (the `expanded` reporter the
|
||||
# runner picks when stdout isn't a TTY — which buries real failures in
|
||||
# thousands of pass lines). Override with TEST_REPORTER=expanded when
|
||||
# debugging a specific run. (T-242)
|
||||
REPORTER="${TEST_REPORTER:-failures-only}"
|
||||
|
||||
coverage=0
|
||||
[[ "${1:-}" == "--coverage" ]] && coverage=1
|
||||
|
||||
echo "==> flutter analyze"
|
||||
flutter analyze
|
||||
|
||||
echo "==> dart format (whole tree)"
|
||||
dart format --set-exit-if-changed .
|
||||
|
||||
echo "==> dart test (forkpty — incompatible with flutter test runner)"
|
||||
dart test --tags forkpty test/pty/session_test.dart test/panes/registry_test.dart
|
||||
echo "==> dart test (pty — unreliable under the flutter test runner; serial)"
|
||||
# --concurrency=1: these spawn real PTYs and compete for fds when run in
|
||||
# parallel, which flaked them (registry/session). Serialize — the proper fix
|
||||
# for resource-bound tests, vs. the old per-test `retry:` band-aid. (T-193)
|
||||
dart test -r "$REPORTER" --concurrency=1 --tags pty test/pty/session_test.dart test/panes/registry_test.dart
|
||||
|
||||
echo "==> flutter test --coverage (unit + widget + golden)"
|
||||
flutter test --coverage --exclude-tags forkpty
|
||||
# The parallel pool excludes both pty (runs under dart test, above) and
|
||||
# serial-tagged tests (concurrency-vulnerable — run in their own --concurrency=1
|
||||
# pass below). See dart_test.yaml + T-193.
|
||||
if [[ "$coverage" == 1 ]]; then
|
||||
# Each pass writes its raw coverage into a per-run temp dir (via
|
||||
# --coverage-path), never the shared coverage/lcov.info / lcov.parallel.info.
|
||||
# So a concurrent `flutter test --coverage` — a second push gate, or a
|
||||
# `make test` during a push — can't race or delete this run's intermediates
|
||||
# (which crashed merge_lcov with FileNotFoundError). Only the final merged
|
||||
# result lands in coverage/lcov.info, via an atomic rename within coverage/.
|
||||
# (T-345)
|
||||
COV_TMP="$(mktemp -d "${TMPDIR:-/tmp}/clide-cov.XXXXXX")"
|
||||
trap 'rm -rf "$COV_TMP" "coverage/.lcov.$$.info"' EXIT
|
||||
echo "==> flutter test --coverage (parallel pool; excludes pty + serial)"
|
||||
flutter test -r "$REPORTER" --coverage --coverage-path "$COV_TMP/parallel.info" --exclude-tags "pty || serial" --timeout 60s
|
||||
echo "==> flutter test --coverage (serial-tagged; --concurrency=1)"
|
||||
flutter test -r "$REPORTER" --coverage --coverage-path "$COV_TMP/serial.info" --tags serial --concurrency=1 --timeout 60s
|
||||
echo "==> merge coverage (parallel + serial passes → coverage/lcov.info)"
|
||||
mkdir -p coverage
|
||||
python3 ci/merge_lcov.py "$COV_TMP/parallel.info" "$COV_TMP/serial.info" > "coverage/.lcov.$$.info"
|
||||
mv -f "coverage/.lcov.$$.info" coverage/lcov.info
|
||||
else
|
||||
echo "==> flutter test (dev; parallel pool, excludes pty + serial)"
|
||||
flutter test -r "$REPORTER" --exclude-tags "pty || serial" --concurrency=12 --timeout 60s
|
||||
echo "==> flutter test (dev; serial-tagged, --concurrency=1)"
|
||||
flutter test -r "$REPORTER" --tags serial --concurrency=1 --timeout 60s
|
||||
fi
|
||||
|
||||
@@ -5,4 +5,6 @@ set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "==> a11y suite"
|
||||
flutter test test/a11y/
|
||||
# failures-only: failing tests + a final count, not one line per test.
|
||||
# Override with TEST_REPORTER=expanded when debugging. (T-242)
|
||||
flutter test -r "${TEST_REPORTER:-failures-only}" test/a11y/
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
# ci/test_core.sh — run the Flutter-free core Dart tests.
|
||||
#
|
||||
# Covers `test/` at the repo root (IPC, daemon, PTY). Wraps `dart test`
|
||||
# in a hard timeout + process-group kill so a hanging test (typically
|
||||
# one holding a native fd open) can't wedge CI or pre-push.
|
||||
# Covers `test/` at the repo root (IPC, daemon, PTY, git, panes, files,
|
||||
# editor, pql). Each pass runs under `dart test --timeout` so a hanging
|
||||
# test (typically one holding a native fd open) fails fast instead of
|
||||
# wedging CI or the pre-push gate — the same portable mechanism ci/test.sh
|
||||
# uses for the Flutter suite. No external `timeout`/`setsid` wrapper: those
|
||||
# are GNU coreutils and absent on macOS, where their failure silently
|
||||
# skipped the whole suite.
|
||||
#
|
||||
# Rationale: D-030 makes tests client-side only; a hang here is always
|
||||
# local — either a real bug or a bad test. Either way we'd rather fail
|
||||
# loudly at 120s than block a pre-push indefinitely.
|
||||
# loudly at the per-test timeout than block a pre-push indefinitely.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -20,26 +24,37 @@ if ! command -v dart >/dev/null; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Hard timeout (seconds). The PTY tests should finish in <5s; IPC/daemon
|
||||
# tests are faster still. 120s is generous for CI warmup, tiny for a
|
||||
# hang.
|
||||
TIMEOUT_SECONDS=${TIMEOUT_SECONDS:-120}
|
||||
# Per-test hard timeout. The PTY tests should finish in <5s; IPC/daemon
|
||||
# tests are faster still. 60s is generous for CI warmup, tiny for a hang.
|
||||
# Matches ci/test.sh's --timeout 60s.
|
||||
TEST_TIMEOUT="${TEST_TIMEOUT:-60s}"
|
||||
|
||||
# failures-only: print failing tests + a final count, not one line per test.
|
||||
# Override with TEST_REPORTER=expanded when debugging. (T-242)
|
||||
REPORTER="${TEST_REPORTER:-failures-only}"
|
||||
|
||||
# Run dart test in its own process group so we can kill descendants on
|
||||
# timeout. `setsid` starts a new session; `timeout --kill-after` SIGKILLs
|
||||
# after SIGTERM if the test ignores it.
|
||||
CORE_DIRS="test/ipc test/pty test/daemon test/git test/panes test/files test/editor test/pql"
|
||||
|
||||
echo "test-core: dart test ${CORE_DIRS} (timeout ${TIMEOUT_SECONDS}s)"
|
||||
if ! timeout --kill-after=5s "${TIMEOUT_SECONDS}s" \
|
||||
setsid --wait dart test $CORE_DIRS ; then
|
||||
rc=$?
|
||||
if [[ $rc -eq 124 ]]; then
|
||||
echo "test-core: TIMEOUT — killing descendants" >&2
|
||||
pkill -9 -f "dart test test/" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
exit $rc
|
||||
fi
|
||||
# Run a `dart test` pass under the per-test timeout. set -e propagates a
|
||||
# failing pass (including a --timeout-induced failure) with dart's exit code.
|
||||
run_pass() {
|
||||
dart test -r "$REPORTER" --timeout "$TEST_TIMEOUT" "$@"
|
||||
}
|
||||
|
||||
# Some core tests must not share the parallel pool:
|
||||
# - pty: spawn real PTYs (posix_openpt + posix_spawn) + a reader isolate;
|
||||
# in the parallel pool they contend for fds + CPU and the isolate starves,
|
||||
# flaking 'write sends keystrokes to child' (T-292).
|
||||
# - serial: spawn real `pql` processes against the shared on-disk
|
||||
# `.pql/pql.db`; concurrent invocations contend for the SQLite lock and
|
||||
# flake with PqlException(69) (db busy). (T-193, surfaced by the pql 1.10
|
||||
# record_id migration.)
|
||||
# Run both in one --concurrency=1 pass (matching ci/test.sh's serial handling),
|
||||
# then everything else in parallel.
|
||||
echo "test-core: dart test (pty + serial; --concurrency=1) (timeout ${TEST_TIMEOUT})"
|
||||
run_pass --concurrency=1 --tags "pty || serial" $CORE_DIRS
|
||||
|
||||
echo "test-core: dart test (rest; parallel, excludes pty + serial) (timeout ${TEST_TIMEOUT})"
|
||||
run_pass --exclude-tags "pty || serial" $CORE_DIRS
|
||||
|
||||
echo "test-core: ok"
|
||||
|
||||
@@ -3,16 +3,10 @@
|
||||
# start" regression gate. Flutter integration tests prefer one file at
|
||||
# a time on desktop; we iterate to avoid the "Unable to start the app"
|
||||
# error that hits when they run as a batch.
|
||||
#
|
||||
# Skips: theme_picker_test.dart — pumpAndSettle hangs on theme.pick
|
||||
# (T-116). Restore once that's fixed.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
for f in integration_test/*_test.dart; do
|
||||
case "$f" in
|
||||
integration_test/theme_picker_test.dart) echo "==> integration_test: $f (SKIPPED — T-116)"; continue ;;
|
||||
esac
|
||||
echo "==> integration_test: $f"
|
||||
flutter test "$f"
|
||||
done
|
||||
|
||||
@@ -3,8 +3,17 @@
|
||||
# both honor them. Undeclared tags are ignored by the runners, which
|
||||
# silently breaks selective excludes.
|
||||
tags:
|
||||
# Tests that call `forkpty()` via Dart FFI. Must run under `dart test`,
|
||||
# not `flutter test` — the latter's runner hosts a multi-threaded
|
||||
# Flutter engine in which forkpty produces a master fd that never
|
||||
# delivers output. See `test/pty/session_test.dart`.
|
||||
forkpty:
|
||||
# Native-PTY tests (posix_openpt + posix_spawn via Dart FFI). Must run
|
||||
# under `dart test`, not `flutter test`: the flutter-test runner hosts a
|
||||
# multi-threaded engine in which the PTY master fd doesn't reliably
|
||||
# deliver output, so these flake there (verified). They are reliable
|
||||
# under `dart test`. (Tag was historically named `forkpty`, before
|
||||
# T-96 replaced forkpty with posix_spawn.) See `test/pty/session_test.dart`.
|
||||
pty:
|
||||
# Concurrency-vulnerable tests that flake in the parallel flutter pool
|
||||
# (e.g. MessageBus delivery races). The parallel run excludes them
|
||||
# (`--exclude-tags "pty || serial"`); ci/test.sh runs a separate
|
||||
# `--tags serial --concurrency=1` pass and merges its coverage. Prefer a
|
||||
# deterministic await first — only tag `serial` when isolation is the real
|
||||
# fix. (T-193)
|
||||
serial:
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# Codebase Cleanliness & Pattern Audit
|
||||
|
||||
**Date:** 2026-05-26
|
||||
**Scope:** `lib/` (314 files, ~2.1MB) and `pubspec.yaml`. Skipped `legacy/`, `tests/`, generated files. Conventions grounded in [`CLAUDE.md`](CLAUDE.md) guardrails and the [`governance/decisions/`](governance/decisions/) D-records (architecture, extensions, testing, tooling, process).
|
||||
|
||||
## Baseline health
|
||||
|
||||
- `dart format --set-exit-if-changed`: **468/468 files clean.**
|
||||
- `flutter analyze`: **3 warnings, all in a single WIP file** (`lib/builtin/claude/src/session_orchestrator.dart`), already in the modified-but-uncommitted set:
|
||||
- L15: unused `dart:convert` import
|
||||
- L16: unused `dart:io` import
|
||||
- L29: unused private field `_resumeTailBytes`
|
||||
- No `print(` / `debugPrint(` in `lib/`. No TODO / FIXME / HACK comments. No commented-out code blocks. No orphaned `.dart` files.
|
||||
|
||||
The repo is in unusually good baseline hygiene shape — the audit's interesting findings are structural, not janitorial.
|
||||
|
||||
## Guardrail / D-record compliance
|
||||
|
||||
| Decision | Status | Notes |
|
||||
|---|---|---|
|
||||
| **D-7** bare `WidgetsApp` | Pass | No `MaterialApp`/`CupertinoApp`/`Scaffold`/`ElevatedButton`. Material/Cupertino imports exist only in inlined xterm heritage under `lib/src/terminal/` and aren't instantiated. |
|
||||
| **D-8** feature-first, barrels only | Pass | No cross-feature reach into another feature's `src/`. `lib/extension/src/` → `lib/kernel/src/` is the one cross-`src/` link (extension framework on platform foundation — legitimate). |
|
||||
| **D-10** ChangeNotifier + ListenableBuilder | Pass | No provider/riverpod/bloc/get_it. Three `InheritedWidget` subclasses (`ScrollbarTheme`, `ClideKernel`, `ClideTheme` via `InheritedNotifier<ThemeController>`) are all justified scoped-context uses. |
|
||||
| **D-31 / D-42** exact-pin + `licenses.yaml` | Pass | No caret ranges in `pubspec.yaml`. All 13 runtime deps + dev deps + native binaries documented. |
|
||||
| **D-46** core frame vs shipped extensions | **Drift** | `editor`, `claude`, `claude-control`, `markdown`, `diff`, `git-ui`, `pql`, `canvas`, `graph`, `decisions`, `tickets`, `todos`, `problems` should be shipped extensions on a separate registration path. They still live in `lib/builtin/` alongside core frame builtins. Architectural intent documented but not enforced — migration deferred. |
|
||||
| **D-56** single package, in-process IPC | Pass | No `bin/clide.dart`, no `app/`, no `sidecar/`. `lib/src/daemon/` is in-process dispatcher + handlers. |
|
||||
| **D-1 / D-68** CLI primary, MCP secondary | Pass | `lib/src/ipc/server.dart` (unix socket) and `lib/src/ipc/mcp_server.dart` (HTTP+SSE) both wrap the same `DaemonDispatcher`. |
|
||||
| **D-72** serial dispatch on main isolate | Pass | `server.dart:212` awaits `dispatcher.dispatch(req)` inside a per-client serial line handler. |
|
||||
| **D-74** schema co-registered | Pass | `DaemonDispatcher` accepts `CommandSchema?` at registration (`dispatcher.dart:23`), validates pre-handler (L55–59). |
|
||||
| **D-66** 95% coverage floor | Pass | `pubspec.yaml:26`: `coverage_floor: 95`. |
|
||||
| **D-75 / D-77 / D-78** Claude coupling isolation | Pass | All `~/.claude/`, transcript JSONL, `claude` CLI invocations live behind `lib/builtin/claude/src/`. No leakage to other features. |
|
||||
| **CLAUDE.md** in-house renderers | Pass | Markdown: pub.dev parser (per D-58) + custom renderer in `lib/widgets/src/clide_markdown.dart`. Canvas + graph in-house. |
|
||||
| **Analyzer suppressions** | Pass | `// ignore`s are confined to xterm heritage code, FFI bindings (C naming), and lookup tables. All justified. |
|
||||
|
||||
## Findings worth acting on
|
||||
|
||||
### 1. Unused pubspec dependencies (D-31 violation in spirit)
|
||||
|
||||
- `flutter_widget_from_html_core: 0.17.2` — listed in `dependencies:`, imported nowhere. Mentioned in D-58 as adoptable, but no current consumer.
|
||||
- `mocktail: 1.0.4` — listed in `dev_dependencies:`, imported nowhere. D-25 specifies it for IO mocks, but no tests use it today.
|
||||
|
||||
Per D-62 (Dependency removal process), removing requires the full 5-step PR. But carrying them violates D-31's "what stays is exact-pinned" intent — the spirit being "we keep only what we use."
|
||||
|
||||
### 2. D-46 architectural drift — known but uncodified
|
||||
|
||||
Thirteen "shipped extension" features still register through the core frame path. This is documented drift, not new — but it's the largest unresolved architectural debt. A dedicated `lib/extensions/` directory + a second registration tier is the prescribed fix.
|
||||
|
||||
### 3. Three hotspot files (>800 lines, mixed concerns)
|
||||
|
||||
| File | Lines | Shape |
|
||||
|---|---|---|
|
||||
| `lib/app.dart` | 1175 | 11+ State classes for sidebar/workspace/editor/context/status — split by region |
|
||||
| `lib/src/terminal/src/core/escape/parser.dart` | 1139 | `EscapeParser` FSM, 1095-line class — handler logic could split by escape domain |
|
||||
| `lib/src/terminal/src/terminal.dart` | 907 | `Terminal` class implementing 5 interfaces, 80+ methods spanning cursor/buffer/input/output |
|
||||
|
||||
The terminal pair is partially inlined heritage code (xterm.dart) so refactoring it competes with merge-friendliness; `app.dart` is yours to split freely.
|
||||
|
||||
### 4. Silent error swallowing in `lib/builtin/claude/src/claude_config.dart`
|
||||
|
||||
Seven `catch (_)` sites (L292, 301, 314, 394, 460, 489, 505) in config/skill loading. Config parsing failures vanish without log or UI signal. Per D-76 the service is supposed to "degrade gracefully" on parse miss — that's fine, but at minimum these should log to make schema drift visible (D-75 / D-78 explicitly call out that detection is the mitigation for CC-internals drift).
|
||||
|
||||
### 5. Long methods — theme resolvers and one claude config probe
|
||||
|
||||
| File:Line | Method | Lines |
|
||||
|---|---|---|
|
||||
| `lib/kernel/src/theme/resolver.dart:9` | `palette()` | 172 |
|
||||
| `lib/kernel/src/theme/resolver.dart:11` | `semantic()` | 170 |
|
||||
| `lib/kernel/src/theme/resolver.dart:13` | `surface()` | 168 |
|
||||
| `lib/builtin/claude/src/claude_config.dart:306` | `_parseInitProbe()` | 178 |
|
||||
|
||||
Theme resolvers are dense token tables (data-shaped, not control-flow) — borderline but tolerable. The probe parser is the strongest splitting candidate.
|
||||
|
||||
### 6. Suspicious duplication
|
||||
|
||||
- `lib/builtin/tickets/src/tickets_view.dart` + `ticket_detail_view.dart` — 4+ near-identical `builder: (ctx, hovered, _) => ...` responsive button scaffolds. Extract a factory widget.
|
||||
|
||||
### 7. Magic strings worth a constant
|
||||
|
||||
- `'type': 'request' | 'response' | 'event'` recurs across `lib/src/ipc/envelope.dart` (L42, 79, 141). With two transports (socket + MCP) both speaking JSON envelopes, these belong as enum-or-const so a typo can't silently land.
|
||||
- `lib/builtin/claude/src/transcript_reader.dart` (L477, 495, 498): `'user'`, `'assistant'`, `'permission-mode'` — strong candidates for an enum per the D-75 isolation principle (one place that knows the schema).
|
||||
|
||||
### 8. Coupling hub
|
||||
|
||||
`lib/main.dart` imports from 51 files — boot orchestrator, expected, but fragile. Splittable into `boot_kernel.dart` / `boot_ipc.dart` / `boot_ui.dart` if it grows further.
|
||||
|
||||
## What's notably *not* a problem
|
||||
|
||||
- No god-object utilities (`utils.dart`, `helpers.dart`, etc.) — feature-first discipline holds.
|
||||
- No unchecked `as` casts — every cast is guarded by `is` or null-coalesce.
|
||||
- No `dynamic` overuse outside legitimate JSON / Flutter API boundaries.
|
||||
- Naming is uniformly Dart-idiomatic across 314 files.
|
||||
- No deep control-flow nesting outside idiomatic `build()` trees.
|
||||
|
||||
## Recommended next moves
|
||||
|
||||
1. Land the three analyzer warnings in `session_orchestrator.dart` as part of the in-flight commit.
|
||||
2. Decide on `flutter_widget_from_html_core` and `mocktail` — either wire them in or remove them via the D-62 process.
|
||||
3. Open a tracking ticket for the D-46 migration if one doesn't exist; this is the only meaningful drift.
|
||||
4. Split `app.dart` by layout region — lowest-risk, highest-readability win.
|
||||
5. Add logging (not exception propagation) to the `claude_config.dart` silent catches so schema drift surfaces during real use.
|
||||
6. Extract envelope type strings to constants/enum in `lib/src/ipc/envelope.dart` before the second transport (MCP) accumulates more divergence.
|
||||
|
||||
## Overall assessment
|
||||
|
||||
This is a tidy codebase. The audit found one architectural drift (D-46, already documented), three localized hotspots, two stale pubspec entries, and a handful of cosmetic improvements. Nothing systemic.
|
||||
@@ -33,9 +33,12 @@ One OS process. The Flutter app hosts:
|
||||
- every subsystem handler (pane/files/editor/git/pql),
|
||||
- the extension manager and all built-in extensions.
|
||||
|
||||
`tmux` is the only external long-lived process — it owns Claude
|
||||
session persistence so panes survive app restarts (D-41). The app
|
||||
re-attaches via `tmux new-session -A` on boot.
|
||||
The Claude pane is driven over Claude Code's stream-json stdio control
|
||||
protocol — clide spawns the `claude` child directly and renders its
|
||||
event stream natively (D-75/D-77/D-78). Session continuity is
|
||||
`--resume <session-id>` (state lives in Claude's transcript files), not
|
||||
a long-lived wrapper process. `tmux` is no longer in the Claude path; it
|
||||
is retained only by the general-purpose terminal builtin.
|
||||
|
||||
PTYs are spawned natively from Dart. `lib/src/pty/native_pty.dart`
|
||||
calls `posix_openpt()` + `posix_spawn()` via FFI; the child inherits
|
||||
@@ -74,11 +77,12 @@ state-changing command emits one or more events on a long-lived
|
||||
event stream; every UI affordance has a matching CLI verb. See D-6
|
||||
for the subsystem/verb/event contract.
|
||||
|
||||
> **Caveat (2026-05):** the Unix-socket server that exposes the
|
||||
> dispatcher to a thin `clide` C client is currently unimplemented.
|
||||
> Today's working path is in-process direct dispatch. See **T-99**
|
||||
> (IPC server implementation) and **D-68** (dual integration surface
|
||||
> — Bash CLI primary, MCP secondary).
|
||||
The Unix-socket server that exposes the dispatcher to a thin `clide` C
|
||||
client (`native/clide-cli/clide.c`) is implemented in
|
||||
`lib/src/ipc/server.dart`; the socket path, access control, and dispatch
|
||||
model are pinned by D-70/D-71/D-72. In-process direct dispatch remains
|
||||
the path for the Flutter app's own subsystem calls. See **D-68** (dual
|
||||
integration surface — Bash CLI primary, MCP secondary).
|
||||
|
||||
### User-facing — Flutter desktop
|
||||
|
||||
|
||||
@@ -71,112 +71,112 @@ class ClideTheme {
|
||||
|
||||
// ─── ThemeData ────────────────────────────────────────────────────────
|
||||
static ThemeData get data => ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
scaffoldBackgroundColor: _bg,
|
||||
canvasColor: _bg,
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
scaffoldBackgroundColor: _bg,
|
||||
canvasColor: _bg,
|
||||
|
||||
colorScheme: const ColorScheme.dark(
|
||||
brightness: Brightness.dark,
|
||||
primary: _accent,
|
||||
onPrimary: Color(0xFF0D1020),
|
||||
secondary: _synKeyword,
|
||||
onSecondary: Color(0xFF0D1020),
|
||||
surface: _surface,
|
||||
onSurface: _textHi,
|
||||
surfaceContainerHighest: _surfaceHi,
|
||||
outline: _border,
|
||||
outlineVariant: _borderHi,
|
||||
error: _err,
|
||||
onError: Color(0xFF0D1020),
|
||||
),
|
||||
colorScheme: const ColorScheme.dark(
|
||||
brightness: Brightness.dark,
|
||||
primary: _accent,
|
||||
onPrimary: Color(0xFF0D1020),
|
||||
secondary: _synKeyword,
|
||||
onSecondary: Color(0xFF0D1020),
|
||||
surface: _surface,
|
||||
onSurface: _textHi,
|
||||
surfaceContainerHighest: _surfaceHi,
|
||||
outline: _border,
|
||||
outlineVariant: _borderHi,
|
||||
error: _err,
|
||||
onError: Color(0xFF0D1020),
|
||||
),
|
||||
|
||||
textTheme: const TextTheme(
|
||||
// Josefin Sans Light for display; JetBrains Mono for code/body.
|
||||
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, height: 1.1, letterSpacing: 0.2, color: _textHi),
|
||||
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, height: 1.15, letterSpacing: 0.2, color: _textHi),
|
||||
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, height: 1.2, letterSpacing: 0.2, color: _textHi),
|
||||
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, height: 1.3, letterSpacing: 0.3, color: _text),
|
||||
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, height: 1.2, letterSpacing: 1.0, color: _textMute),
|
||||
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
|
||||
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, height: 1.4, color: _text),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
// Josefin Sans Light for display; JetBrains Mono for code/body.
|
||||
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, height: 1.1, letterSpacing: 0.2, color: _textHi),
|
||||
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, height: 1.15, letterSpacing: 0.2, color: _textHi),
|
||||
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, height: 1.2, letterSpacing: 0.2, color: _textHi),
|
||||
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, height: 1.3, letterSpacing: 0.3, color: _text),
|
||||
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, height: 1.2, letterSpacing: 1.0, color: _textMute),
|
||||
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
|
||||
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, height: 1.4, color: _text),
|
||||
),
|
||||
|
||||
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
|
||||
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
|
||||
|
||||
cardTheme: CardThemeData(
|
||||
color: _surface,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: const BorderSide(color: _border),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
margin: EdgeInsets.zero,
|
||||
),
|
||||
cardTheme: CardThemeData(
|
||||
color: _surface,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: const BorderSide(color: _border),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
margin: EdgeInsets.zero,
|
||||
),
|
||||
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: _bgSunken,
|
||||
hintStyle: const TextStyle(color: _textMute),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: const BorderSide(color: _border),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: const BorderSide(color: _border),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: const BorderSide(color: _accent, width: 1.5),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: _bgSunken,
|
||||
hintStyle: const TextStyle(color: _textMute),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: const BorderSide(color: _border),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: const BorderSide(color: _border),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: const BorderSide(color: _accent, width: 1.5),
|
||||
),
|
||||
),
|
||||
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: _accent,
|
||||
foregroundColor: const Color(0xFF0D1020),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
|
||||
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 12, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: _accent,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 12),
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: _accent,
|
||||
foregroundColor: const Color(0xFF0D1020),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
|
||||
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 12, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: _accent,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 12),
|
||||
),
|
||||
),
|
||||
|
||||
// Pill-style chips (matches the Projects row in the dashboard).
|
||||
chipTheme: ChipThemeData(
|
||||
backgroundColor: _surface,
|
||||
side: const BorderSide(color: _borderHi),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
|
||||
labelStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 11, color: _text),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
),
|
||||
// Pill-style chips (matches the Projects row in the dashboard).
|
||||
chipTheme: ChipThemeData(
|
||||
backgroundColor: _surface,
|
||||
side: const BorderSide(color: _borderHi),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
|
||||
labelStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 11, color: _text),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
),
|
||||
|
||||
iconTheme: const IconThemeData(color: _textDim, size: 14),
|
||||
iconTheme: const IconThemeData(color: _textDim, size: 14),
|
||||
|
||||
tooltipTheme: TooltipThemeData(
|
||||
decoration: BoxDecoration(
|
||||
color: _surfaceHi,
|
||||
border: Border.all(color: _borderHi),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 11, color: _textHi),
|
||||
),
|
||||
tooltipTheme: TooltipThemeData(
|
||||
decoration: BoxDecoration(
|
||||
color: _surfaceHi,
|
||||
border: Border.all(color: _borderHi),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 11, color: _textHi),
|
||||
),
|
||||
|
||||
scrollbarTheme: ScrollbarThemeData(
|
||||
thumbColor: WidgetStatePropertyAll(_border),
|
||||
thickness: const WidgetStatePropertyAll(6),
|
||||
radius: const Radius.circular(3),
|
||||
),
|
||||
);
|
||||
scrollbarTheme: ScrollbarThemeData(
|
||||
thumbColor: WidgetStatePropertyAll(_border),
|
||||
thickness: const WidgetStatePropertyAll(6),
|
||||
radius: const Radius.circular(3),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Raw color tokens — use when Material widgets can't carry the meaning.
|
||||
|
||||
@@ -63,33 +63,33 @@ class MidnightTheme {
|
||||
);
|
||||
|
||||
static ThemeData get data => ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
scaffoldBackgroundColor: _bg,
|
||||
canvasColor: _bg,
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: _accent,
|
||||
onPrimary: Color(0xFF0B1220),
|
||||
secondary: _synKeyword,
|
||||
onSecondary: Color(0xFF0B1220),
|
||||
surface: _surface,
|
||||
onSurface: _textHi,
|
||||
surfaceContainerHighest: _surfaceHi,
|
||||
outline: _border,
|
||||
outlineVariant: _borderHi,
|
||||
error: _err,
|
||||
onError: Color(0xFF0B1220),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
|
||||
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
|
||||
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
|
||||
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
|
||||
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textMute),
|
||||
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
|
||||
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
|
||||
iconTheme: const IconThemeData(color: _textDim, size: 14),
|
||||
);
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
scaffoldBackgroundColor: _bg,
|
||||
canvasColor: _bg,
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: _accent,
|
||||
onPrimary: Color(0xFF0B1220),
|
||||
secondary: _synKeyword,
|
||||
onSecondary: Color(0xFF0B1220),
|
||||
surface: _surface,
|
||||
onSurface: _textHi,
|
||||
surfaceContainerHighest: _surfaceHi,
|
||||
outline: _border,
|
||||
outlineVariant: _borderHi,
|
||||
error: _err,
|
||||
onError: Color(0xFF0B1220),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
|
||||
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
|
||||
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
|
||||
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
|
||||
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textMute),
|
||||
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
|
||||
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
|
||||
iconTheme: const IconThemeData(color: _textDim, size: 14),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,33 +65,33 @@ class PaperTheme {
|
||||
);
|
||||
|
||||
static ThemeData get data => ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.light,
|
||||
scaffoldBackgroundColor: _bg,
|
||||
canvasColor: _bg,
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: _accent,
|
||||
onPrimary: Color(0xFFFBF8F1),
|
||||
secondary: _info,
|
||||
onSecondary: Color(0xFFFBF8F1),
|
||||
surface: _surface,
|
||||
onSurface: _textHi,
|
||||
surfaceContainerHighest: _surfaceHi,
|
||||
outline: _border,
|
||||
outlineVariant: _borderHi,
|
||||
error: _err,
|
||||
onError: Color(0xFFFBF8F1),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
|
||||
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
|
||||
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
|
||||
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
|
||||
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textDim),
|
||||
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
|
||||
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
|
||||
iconTheme: const IconThemeData(color: _text, size: 14),
|
||||
);
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.light,
|
||||
scaffoldBackgroundColor: _bg,
|
||||
canvasColor: _bg,
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: _accent,
|
||||
onPrimary: Color(0xFFFBF8F1),
|
||||
secondary: _info,
|
||||
onSecondary: Color(0xFFFBF8F1),
|
||||
surface: _surface,
|
||||
onSurface: _textHi,
|
||||
surfaceContainerHighest: _surfaceHi,
|
||||
outline: _border,
|
||||
outlineVariant: _borderHi,
|
||||
error: _err,
|
||||
onError: Color(0xFFFBF8F1),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
|
||||
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
|
||||
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
|
||||
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
|
||||
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textDim),
|
||||
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
|
||||
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
|
||||
iconTheme: const IconThemeData(color: _text, size: 14),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,34 +65,34 @@ class TerminalTheme {
|
||||
);
|
||||
|
||||
static ThemeData get data => ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
scaffoldBackgroundColor: _bg,
|
||||
canvasColor: _bg,
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: _accent,
|
||||
onPrimary: Color(0xFF000000),
|
||||
secondary: _info,
|
||||
onSecondary: Color(0xFF000000),
|
||||
surface: _surface,
|
||||
onSurface: _textHi,
|
||||
surfaceContainerHighest: _surfaceHi,
|
||||
outline: _border,
|
||||
outlineVariant: _borderHi,
|
||||
error: _err,
|
||||
onError: Color(0xFF000000),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
|
||||
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
|
||||
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
|
||||
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
|
||||
// Terminal mockups go full-mono even for display.
|
||||
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textMute),
|
||||
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
|
||||
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
|
||||
iconTheme: const IconThemeData(color: _textDim, size: 14),
|
||||
);
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
scaffoldBackgroundColor: _bg,
|
||||
canvasColor: _bg,
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: _accent,
|
||||
onPrimary: Color(0xFF000000),
|
||||
secondary: _info,
|
||||
onSecondary: Color(0xFF000000),
|
||||
surface: _surface,
|
||||
onSurface: _textHi,
|
||||
surfaceContainerHighest: _surfaceHi,
|
||||
outline: _border,
|
||||
outlineVariant: _borderHi,
|
||||
error: _err,
|
||||
onError: Color(0xFF000000),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
|
||||
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
|
||||
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
|
||||
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
|
||||
// Terminal mockups go full-mono even for display.
|
||||
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textMute),
|
||||
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
|
||||
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
|
||||
iconTheme: const IconThemeData(color: _textDim, size: 14),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,13 +10,7 @@
|
||||
import 'dart:ui' show Color;
|
||||
|
||||
class ClideTheme {
|
||||
const ClideTheme({
|
||||
required this.name,
|
||||
required this.dark,
|
||||
required this.subtitle,
|
||||
required this.palette,
|
||||
required this.syntax,
|
||||
});
|
||||
const ClideTheme({required this.name, required this.dark, required this.subtitle, required this.palette, required this.syntax});
|
||||
final String name;
|
||||
final bool dark;
|
||||
final String subtitle;
|
||||
|
||||
@@ -79,8 +79,8 @@ The widget is a thin shell:
|
||||
- Calls `bodyBuilder(active)` for the visible content
|
||||
- Routes user gestures to controller methods or callbacks
|
||||
- Emits `onCloseRequested` / `onAddRequested` so the host decides
|
||||
the actual lifecycle (e.g. Claude pane spawns a new tmux session,
|
||||
doesn't just append a UI tab)
|
||||
the actual lifecycle (e.g. the Claude pane spawns a new stream-json
|
||||
session, doesn't just append a UI tab)
|
||||
|
||||
The host owns the controller and the payload type. The widget never
|
||||
touches PTY, IPC, or Claude session naming.
|
||||
@@ -138,7 +138,7 @@ ClaudePane (host)
|
||||
)
|
||||
```
|
||||
|
||||
`ClaudeSessionRef` carries the tmux session name + isPrimary. The
|
||||
`ClaudeSessionRef` carries the stream-json session id + isPrimary. The
|
||||
controller is seeded with `[primary]` on boot; secondaries get
|
||||
appended as the user clicks `+`. Closing a secondary triggers
|
||||
`pane.close` IPC and removes the entry; closing the primary is not
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"name": "ClideCollapserCard",
|
||||
"shapes": {
|
||||
"title": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 28,
|
||||
"text": "ClideCollapserCard — one collapser primitive for all group / tool cards",
|
||||
"fontColor": "#E2E8F5", "fontSize": 18
|
||||
},
|
||||
"subtitle": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 56,
|
||||
"text": "Each card type grabs the same widget (no cross-type collapsing) · color property drives border + label/text · fixed-width counter slot · status icon hard against the card edge",
|
||||
"fontColor": "#6A7280", "fontSize": 12
|
||||
},
|
||||
|
||||
"lbl-collapsed": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 100,
|
||||
"text": "COLLAPSED (ticker row = toggle, focusable)",
|
||||
"fontColor": "#6A7280", "fontSize": 11
|
||||
},
|
||||
|
||||
"a1": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 124, "width": 660, "height": 46,
|
||||
"fillColor": "#21262F", "strokeColor": "#393E48", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"a1-chev": { "type": "Text", "parent": "a1", "left": 52, "top": 138, "text": "›", "fontColor": "#6A7280", "fontSize": 16 },
|
||||
"a1-label": { "type": "Text", "parent": "a1", "left": 74, "top": 140, "text": "Activity", "fontColor": "#6A7280", "fontSize": 12 },
|
||||
"a1-summary": { "type": "Text", "parent": "a1", "left": 150, "top": 140, "text": "Read conversation_view.dart", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"a1-count": { "type": "Text", "parent": "a1", "left": 560, "top": 140, "text": "3 steps", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"a1-status": { "type": "Text", "parent": "a1", "left": 676, "top": 139, "text": "✓", "fontColor": "#00AB9A", "fontSize": 14 },
|
||||
|
||||
"a2": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 178, "width": 660, "height": 46,
|
||||
"fillColor": "#21262F", "strokeColor": "#00AB9A", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"a2-chev": { "type": "Text", "parent": "a2", "left": 52, "top": 192, "text": "›", "fontColor": "#00AB9A", "fontSize": 16 },
|
||||
"a2-label": { "type": "Text", "parent": "a2", "left": 74, "top": 194, "text": "Edits", "fontColor": "#00AB9A", "fontSize": 12 },
|
||||
"a2-summary": { "type": "Text", "parent": "a2", "left": 150, "top": 194, "text": "clide_markdown.dart", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"a2-count": { "type": "Text", "parent": "a2", "left": 560, "top": 194, "text": "7 edits", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"a2-status": { "type": "Text", "parent": "a2", "left": 674, "top": 193, "text": "◐", "fontColor": "#00A3D2", "fontSize": 14 },
|
||||
|
||||
"a3": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 232, "width": 660, "height": 46,
|
||||
"fillColor": "#21262F", "strokeColor": "#F06C6F", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"a3-chev": { "type": "Text", "parent": "a3", "left": 52, "top": 246, "text": "›", "fontColor": "#F06C6F", "fontSize": 16 },
|
||||
"a3-label": { "type": "Text", "parent": "a3", "left": 74, "top": 248, "text": "Bash", "fontColor": "#F06C6F", "fontSize": 12 },
|
||||
"a3-summary": { "type": "Text", "parent": "a3", "left": 150, "top": 248, "text": "npm test", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"a3-count": { "type": "Text", "parent": "a3", "left": 560, "top": 248, "text": "1 step", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"a3-status": { "type": "Text", "parent": "a3", "left": 676, "top": 247, "text": "✕", "fontColor": "#F06C6F", "fontSize": 14 },
|
||||
|
||||
"counter-note": { "type": "Text", "left": 470, "top": 96, "text": "counters share a fixed-width right-aligned slot ▾ so the status icon never shifts", "fontColor": "#D08447", "fontSize": 10 },
|
||||
|
||||
"edge-note": { "type": "Text", "left": 470, "top": 286, "text": "status icon hard against the card edge ▲ (was inboard, left of the counter)", "fontColor": "#D08447", "fontSize": 10 },
|
||||
"left-note": { "type": "Text", "left": 40, "top": 286, "text": "chevron hard against the left edge — the toggle (keyboard / AT focusable)", "fontColor": "#D08447", "fontSize": 10 },
|
||||
|
||||
"lbl-expanded": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 332,
|
||||
"text": "EXPANDED (same header; framed body wraps the nested cards — which are the SAME primitive)",
|
||||
"fontColor": "#6A7280", "fontSize": 11
|
||||
},
|
||||
|
||||
"b": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 356, "width": 660, "height": 196,
|
||||
"fillColor": "#21262F", "strokeColor": "#00AB9A", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"b-header": {
|
||||
"type": "Rectangle", "parent": "b",
|
||||
"left": 40, "top": 356, "width": 660, "height": 40,
|
||||
"fillColor": "#292E38", "strokeColor": "#393E48", "corners": [4, 4, 0, 0]
|
||||
},
|
||||
"b-chev": { "type": "Text", "parent": "b-header", "left": 52, "top": 368, "text": "⌄", "fontColor": "#00AB9A", "fontSize": 16 },
|
||||
"b-label": { "type": "Text", "parent": "b-header", "left": 74, "top": 369, "text": "Edits", "fontColor": "#00AB9A", "fontSize": 12 },
|
||||
"b-count": { "type": "Text", "parent": "b-header", "left": 560, "top": 369, "text": "7 edits", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"b-status": { "type": "Text", "parent": "b-header", "left": 676, "top": 368, "text": "✓", "fontColor": "#00AB9A", "fontSize": 14 },
|
||||
|
||||
"sub1": {
|
||||
"type": "Rectangle", "parent": "b",
|
||||
"left": 56, "top": 408, "width": 628, "height": 40,
|
||||
"fillColor": "#1A1E24", "strokeColor": "#393E48", "corners": [3, 3, 3, 3]
|
||||
},
|
||||
"sub1-chev": { "type": "Text", "parent": "sub1", "left": 66, "top": 420, "text": "›", "fontColor": "#6A7280", "fontSize": 14 },
|
||||
"sub1-label": { "type": "Text", "parent": "sub1", "left": 86, "top": 421, "text": "Edit", "fontColor": "#FA5F8B", "fontSize": 11 },
|
||||
"sub1-sum": { "type": "Text", "parent": "sub1", "left": 150, "top": 421, "text": "clide_markdown.dart · _urlLinkSpan", "fontColor": "#6A7280", "fontSize": 10 },
|
||||
"sub1-status": { "type": "Text", "parent": "sub1", "left": 660, "top": 420, "text": "✓", "fontColor": "#00AB9A", "fontSize": 13 },
|
||||
|
||||
"sub2": {
|
||||
"type": "Rectangle", "parent": "b",
|
||||
"left": 56, "top": 454, "width": 628, "height": 40,
|
||||
"fillColor": "#1A1E24", "strokeColor": "#393E48", "corners": [3, 3, 3, 3]
|
||||
},
|
||||
"sub2-chev": { "type": "Text", "parent": "sub2", "left": 66, "top": 466, "text": "›", "fontColor": "#6A7280", "fontSize": 14 },
|
||||
"sub2-label": { "type": "Text", "parent": "sub2", "left": 86, "top": 467, "text": "Edit", "fontColor": "#FA5F8B", "fontSize": 11 },
|
||||
"sub2-sum": { "type": "Text", "parent": "sub2", "left": 150, "top": 467, "text": "clide_markdown.dart · _isHttpUrl", "fontColor": "#6A7280", "fontSize": 10 },
|
||||
"sub2-status": { "type": "Text", "parent": "sub2", "left": 660, "top": 466, "text": "✓", "fontColor": "#00AB9A", "fontSize": 13 },
|
||||
|
||||
"sub3": {
|
||||
"type": "Rectangle", "parent": "b",
|
||||
"left": 56, "top": 500, "width": 628, "height": 40,
|
||||
"fillColor": "#1A1E24", "strokeColor": "#393E48", "corners": [3, 3, 3, 3]
|
||||
},
|
||||
"sub3-chev": { "type": "Text", "parent": "sub3", "left": 66, "top": 512, "text": "›", "fontColor": "#6A7280", "fontSize": 14 },
|
||||
"sub3-label": { "type": "Text", "parent": "sub3", "left": 86, "top": 513, "text": "Edit", "fontColor": "#FA5F8B", "fontSize": 11 },
|
||||
"sub3-sum": { "type": "Text", "parent": "sub3", "left": 150, "top": 513, "text": "clide_markdown.dart · build()", "fontColor": "#6A7280", "fontSize": 10 },
|
||||
"sub3-status": { "type": "Text", "parent": "sub3", "left": 660, "top": 512, "text": "◐", "fontColor": "#00A3D2", "fontSize": 13 },
|
||||
|
||||
"legend": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 572,
|
||||
"text": "Status glyph (right edge): ◐ running (logo-mark spinner) · ✓ success · ✕ error — set per-instance by a single `color` (border + label/text) + a `ClideRunStatus`.",
|
||||
"fontColor": "#6A7280", "fontSize": 11
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 93 KiB |
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "Clide — AskUserQuestion (single)",
|
||||
"shapes": {
|
||||
"panel": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 40, "width": 840, "height": 330,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]
|
||||
},
|
||||
"accent": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 40, "width": 840, "height": 4,
|
||||
"fillColor": "#5aa0e0", "strokeColor": "#5aa0e0", "corners": [8, 8, 0, 0]
|
||||
},
|
||||
"label": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 60,
|
||||
"text": "question", "fontColor": "#5aa0e0", "fontSize": 13
|
||||
},
|
||||
"qtext": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 86,
|
||||
"text": "Which fruit is your favorite?", "fontColor": "#c8d0e0", "fontSize": 17
|
||||
},
|
||||
"opt-banana": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 126, "width": 150, "height": 34,
|
||||
"fillColor": "#27406a", "strokeColor": "#5aa0e0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-banana-t": {
|
||||
"type": "Text", "parent": "panel", "left": 78, "top": 134,
|
||||
"text": "① Banana", "fontColor": "#dbe6f5", "fontSize": 14
|
||||
},
|
||||
"opt-apple": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 224, "top": 126, "width": 130, "height": 34,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-apple-t": {
|
||||
"type": "Text", "parent": "panel", "left": 238, "top": 134,
|
||||
"text": "② Apple", "fontColor": "#c8d0e0", "fontSize": 14
|
||||
},
|
||||
"opt-mango": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 364, "top": 126, "width": 130, "height": 34,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-mango-t": {
|
||||
"type": "Text", "parent": "panel", "left": 378, "top": 134,
|
||||
"text": "③ Mango", "fontColor": "#c8d0e0", "fontSize": 14
|
||||
},
|
||||
"opt-other": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 504, "top": 126, "width": 130, "height": 34,
|
||||
"fillColor": "#1f242c", "strokeColor": "#3a4250", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-other-t": {
|
||||
"type": "Text", "parent": "panel", "left": 518, "top": 134,
|
||||
"text": "Other…", "fontColor": "#9aa4b2", "fontSize": 14
|
||||
},
|
||||
"note-hint": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 176,
|
||||
"text": "✎ add a note to your choice (optional)", "fontColor": "#7a8290", "fontSize": 12
|
||||
},
|
||||
"note-field": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 198, "width": 752, "height": 32,
|
||||
"fillColor": "#14181d", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"note-field-t": {
|
||||
"type": "Text", "parent": "panel", "left": 78, "top": 206,
|
||||
"text": "optional note…", "fontColor": "#5b6470", "fontSize": 13
|
||||
},
|
||||
"divider": {
|
||||
"type": "Line", "parent": "panel", "left": 64, "top": 256, "width": 752, "height": 1,
|
||||
"strokeColor": "#2a3038"
|
||||
},
|
||||
"submit": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 276, "width": 120, "height": 34,
|
||||
"fillColor": "#3b5bdb", "strokeColor": "#3b5bdb", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"submit-t": {
|
||||
"type": "Text", "parent": "panel", "left": 96, "top": 284,
|
||||
"text": "Submit", "fontColor": "#ffffff", "fontSize": 14
|
||||
},
|
||||
"chat": {
|
||||
"type": "Text", "parent": "panel", "left": 700, "top": 284,
|
||||
"text": "› chat instead", "fontColor": "#7a8290", "fontSize": 13
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"name": "Clide — AskUserQuestion (stepper)",
|
||||
"shapes": {
|
||||
"panel": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 40, "width": 840, "height": 330,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]
|
||||
},
|
||||
"accent": {
|
||||
"type": "Rectangle", "left": 40, "top": 40, "width": 840, "height": 4,
|
||||
"fillColor": "#5aa0e0", "strokeColor": "#5aa0e0", "corners": [8, 8, 0, 0]
|
||||
},
|
||||
"nav-left": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 62, "text": "‹", "fontColor": "#7a8290", "fontSize": 16
|
||||
},
|
||||
"chip-pet-t": {
|
||||
"type": "Text", "parent": "panel", "left": 88, "top": 64, "text": "1 · Pet ✓", "fontColor": "#5cb87a", "fontSize": 13
|
||||
},
|
||||
"chip-eaten": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 188, "top": 58, "width": 120, "height": 26,
|
||||
"fillColor": "#27406a", "strokeColor": "#5aa0e0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"chip-eaten-t": {
|
||||
"type": "Text", "parent": "panel", "left": 200, "top": 64, "text": "2 · Eaten", "fontColor": "#dbe6f5", "fontSize": 13
|
||||
},
|
||||
"chip-review-t": {
|
||||
"type": "Text", "parent": "panel", "left": 326, "top": 64, "text": "Review", "fontColor": "#7a8290", "fontSize": 13
|
||||
},
|
||||
"nav-right": {
|
||||
"type": "Text", "parent": "panel", "left": 404, "top": 62, "text": "›", "fontColor": "#7a8290", "fontSize": 16
|
||||
},
|
||||
"step-count": {
|
||||
"type": "Text", "parent": "panel", "left": 770, "top": 64, "text": "2 of 2", "fontColor": "#5b6470", "fontSize": 12
|
||||
},
|
||||
"divider-top": {
|
||||
"type": "Line", "parent": "panel", "left": 64, "top": 94, "width": 752, "height": 1, "strokeColor": "#2a3038"
|
||||
},
|
||||
"header": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 106, "text": "EATEN", "fontColor": "#7a8290", "fontSize": 12
|
||||
},
|
||||
"qtext": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 126, "text": "How do you most often eat your fruit?", "fontColor": "#c8d0e0", "fontSize": 16
|
||||
},
|
||||
"opt-fresh": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 162, "width": 160, "height": 34,
|
||||
"fillColor": "#27406a", "strokeColor": "#5aa0e0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-fresh-t": {
|
||||
"type": "Text", "parent": "panel", "left": 78, "top": 170, "text": "① Fresh / whole", "fontColor": "#dbe6f5", "fontSize": 14
|
||||
},
|
||||
"opt-smoothie": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 234, "top": 162, "width": 140, "height": 34,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-smoothie-t": {
|
||||
"type": "Text", "parent": "panel", "left": 248, "top": 170, "text": "② Smoothie", "fontColor": "#c8d0e0", "fontSize": 14
|
||||
},
|
||||
"opt-salad": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 384, "top": 162, "width": 120, "height": 34,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-salad-t": {
|
||||
"type": "Text", "parent": "panel", "left": 398, "top": 170, "text": "③ Salad", "fontColor": "#c8d0e0", "fontSize": 14
|
||||
},
|
||||
"opt-other": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 514, "top": 162, "width": 120, "height": 34,
|
||||
"fillColor": "#1f242c", "strokeColor": "#3a4250", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-other-t": {
|
||||
"type": "Text", "parent": "panel", "left": 528, "top": 170, "text": "Other…", "fontColor": "#9aa4b2", "fontSize": 14
|
||||
},
|
||||
"note-field": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 212, "width": 752, "height": 30,
|
||||
"fillColor": "#14181d", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"note-field-t": {
|
||||
"type": "Text", "parent": "panel", "left": 78, "top": 219, "text": "✎ add a note (optional)", "fontColor": "#5b6470", "fontSize": 12
|
||||
},
|
||||
"divider-bot": {
|
||||
"type": "Line", "parent": "panel", "left": 64, "top": 262, "width": 752, "height": 1, "strokeColor": "#2a3038"
|
||||
},
|
||||
"back": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 278, "width": 86, "height": 34,
|
||||
"fillColor": "#1f242c", "strokeColor": "#3a4250", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"back-t": {
|
||||
"type": "Text", "parent": "panel", "left": 84, "top": 286, "text": "‹ Back", "fontColor": "#9aa4b2", "fontSize": 14
|
||||
},
|
||||
"next": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 160, "top": 278, "width": 110, "height": 34,
|
||||
"fillColor": "#3b5bdb", "strokeColor": "#3b5bdb", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"next-t": {
|
||||
"type": "Text", "parent": "panel", "left": 188, "top": 286, "text": "Next ›", "fontColor": "#ffffff", "fontSize": 14
|
||||
},
|
||||
"chat": {
|
||||
"type": "Text", "parent": "panel", "left": 700, "top": 286, "text": "› chat instead", "fontColor": "#7a8290", "fontSize": 13
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 39 KiB |
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "Clide — AskUserQuestion (review)",
|
||||
"shapes": {
|
||||
"panel": {
|
||||
"type": "Rectangle", "left": 40, "top": 40, "width": 840, "height": 270,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]
|
||||
},
|
||||
"accent": {
|
||||
"type": "Rectangle", "left": 40, "top": 40, "width": 840, "height": 4,
|
||||
"fillColor": "#5cb87a", "strokeColor": "#5cb87a", "corners": [8, 8, 0, 0]
|
||||
},
|
||||
"label": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 60, "text": "review", "fontColor": "#5cb87a", "fontSize": 13
|
||||
},
|
||||
"header": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 84, "text": "Review your answers", "fontColor": "#c8d0e0", "fontSize": 16
|
||||
},
|
||||
"row1-q": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 124, "text": "Pet", "fontColor": "#7a8290", "fontSize": 13
|
||||
},
|
||||
"row1-a": {
|
||||
"type": "Text", "parent": "panel", "left": 180, "top": 124, "text": "→ Banana — only the big ripe ones", "fontColor": "#c8d0e0", "fontSize": 14
|
||||
},
|
||||
"row2-q": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 152, "text": "Eaten", "fontColor": "#7a8290", "fontSize": 13
|
||||
},
|
||||
"row2-a": {
|
||||
"type": "Text", "parent": "panel", "left": 180, "top": 152, "text": "→ Fresh / whole", "fontColor": "#c8d0e0", "fontSize": 14
|
||||
},
|
||||
"divider": {
|
||||
"type": "Line", "parent": "panel", "left": 64, "top": 196, "width": 752, "height": 1, "strokeColor": "#2a3038"
|
||||
},
|
||||
"back": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 214, "width": 86, "height": 34,
|
||||
"fillColor": "#1f242c", "strokeColor": "#3a4250", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"back-t": {
|
||||
"type": "Text", "parent": "panel", "left": 84, "top": 222, "text": "‹ Back", "fontColor": "#9aa4b2", "fontSize": 14
|
||||
},
|
||||
"submit": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 160, "top": 214, "width": 160, "height": 34,
|
||||
"fillColor": "#2f9e44", "strokeColor": "#2f9e44", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"submit-t": {
|
||||
"type": "Text", "parent": "panel", "left": 188, "top": 222, "text": "Submit answers", "fontColor": "#ffffff", "fontSize": 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "Clide — permission prompt",
|
||||
"shapes": {
|
||||
"panel": {
|
||||
"type": "Rectangle", "left": 40, "top": 40, "width": 840, "height": 300,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]
|
||||
},
|
||||
"accent": {
|
||||
"type": "Rectangle", "left": 40, "top": 40, "width": 840, "height": 4,
|
||||
"fillColor": "#d9a441", "strokeColor": "#d9a441", "corners": [8, 8, 0, 0]
|
||||
},
|
||||
"label": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 60, "text": "permission · Write", "fontColor": "#d9a441", "fontSize": 14
|
||||
},
|
||||
"desc": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 86, "text": "/tmp/banana.txt", "fontColor": "#9aa4b2", "fontSize": 13
|
||||
},
|
||||
"opt-allow": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 118, "width": 120, "height": 34,
|
||||
"fillColor": "#2f9e44", "strokeColor": "#2f9e44", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-allow-t": {
|
||||
"type": "Text", "parent": "panel", "left": 86, "top": 126, "text": "① Allow", "fontColor": "#ffffff", "fontSize": 14
|
||||
},
|
||||
"opt-always": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 194, "top": 118, "width": 230, "height": 34,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-always-t": {
|
||||
"type": "Text", "parent": "panel", "left": 208, "top": 126, "text": "② Allow & don't ask again", "fontColor": "#c8d0e0", "fontSize": 14
|
||||
},
|
||||
"opt-deny": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 434, "top": 118, "width": 110, "height": 34,
|
||||
"fillColor": "#3a2630", "strokeColor": "#c0506a", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"opt-deny-t": {
|
||||
"type": "Text", "parent": "panel", "left": 456, "top": 126, "text": "③ Deny", "fontColor": "#e58ba0", "fontSize": 14
|
||||
},
|
||||
"deny-hint": {
|
||||
"type": "Text", "parent": "panel", "left": 64, "top": 174, "text": "tell Claude what to do differently (optional)", "fontColor": "#7a8290", "fontSize": 12
|
||||
},
|
||||
"deny-field": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 196, "width": 752, "height": 32,
|
||||
"fillColor": "#14181d", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"deny-field-t": {
|
||||
"type": "Text", "parent": "panel", "left": 78, "top": 204, "text": "tell Claude what to do differently…", "fontColor": "#5b6470", "fontSize": 13
|
||||
},
|
||||
"divider": {
|
||||
"type": "Line", "parent": "panel", "left": 64, "top": 252, "width": 752, "height": 1, "strokeColor": "#2a3038"
|
||||
},
|
||||
"send-deny": {
|
||||
"type": "Rectangle", "parent": "panel", "left": 64, "top": 266, "width": 130, "height": 34,
|
||||
"fillColor": "#c0392b", "strokeColor": "#c0392b", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"send-deny-t": {
|
||||
"type": "Text", "parent": "panel", "left": 86, "top": 274, "text": "Deny + note", "fontColor": "#ffffff", "fontSize": 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "Clide — team cockpit sidebar",
|
||||
"shapes": {
|
||||
"panel": {"type": "Rectangle", "left": 40, "top": 24, "width": 340, "height": 740, "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]},
|
||||
|
||||
"h-team": {"type": "Text", "parent": "panel", "left": 58, "top": 40, "text": "TEAM", "fontColor": "#7a8290", "fontSize": 12},
|
||||
|
||||
"d-main": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 70, "width": 9, "height": 9, "fillColor": "#d97757", "strokeColor": "#d97757", "corners": [2, 2, 2, 2]},
|
||||
"n-main": {"type": "Text", "parent": "panel", "left": 74, "top": 66, "text": "main · lead", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-main": {"type": "Text", "parent": "panel", "left": 170, "top": 67, "text": "synthesizing", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-main": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 64, "width": 22, "height": 18, "fillColor": "#1f2a44", "strokeColor": "#5aa0e0", "corners": [3, 3, 3, 3]},
|
||||
"pt-main": {"type": "Text", "parent": "panel", "left": 343, "top": 65, "text": "P", "fontColor": "#5aa0e0", "fontSize": 12},
|
||||
|
||||
"d-tyre": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 104, "width": 9, "height": 9, "fillColor": "#b08cff", "strokeColor": "#b08cff", "corners": [2, 2, 2, 2]},
|
||||
"n-tyre": {"type": "Text", "parent": "panel", "left": 74, "top": 100, "text": "tyre", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-tyre": {"type": "Text", "parent": "panel", "left": 170, "top": 101, "text": "thinking · 38%", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-tyre": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 98, "width": 22, "height": 18, "fillColor": "#1d2e22", "strokeColor": "#5cb87a", "corners": [3, 3, 3, 3]},
|
||||
"pt-tyre": {"type": "Text", "parent": "panel", "left": 343, "top": 99, "text": "A", "fontColor": "#5cb87a", "fontSize": 12},
|
||||
|
||||
"d-burn": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 138, "width": 9, "height": 9, "fillColor": "#e0a04a", "strokeColor": "#e0a04a", "corners": [2, 2, 2, 2]},
|
||||
"n-burn": {"type": "Text", "parent": "panel", "left": 74, "top": 134, "text": "burnelli", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-burn": {"type": "Text", "parent": "panel", "left": 170, "top": 135, "text": "writing · 26%", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-burn": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 132, "width": 22, "height": 18, "fillColor": "#1d2e22", "strokeColor": "#5cb87a", "corners": [3, 3, 3, 3]},
|
||||
"pt-burn": {"type": "Text", "parent": "panel", "left": 343, "top": 133, "text": "A", "fontColor": "#5cb87a", "fontSize": 12},
|
||||
|
||||
"d-gest": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 172, "width": 9, "height": 9, "fillColor": "#e36fb0", "strokeColor": "#e36fb0", "corners": [2, 2, 2, 2]},
|
||||
"n-gest": {"type": "Text", "parent": "panel", "left": 74, "top": 168, "text": "gestalt", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-gest": {"type": "Text", "parent": "panel", "left": 170, "top": 169, "text": "reading · 26%", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-gest": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 166, "width": 22, "height": 18, "fillColor": "#22262e", "strokeColor": "#7a8290", "corners": [3, 3, 3, 3]},
|
||||
"pt-gest": {"type": "Text", "parent": "panel", "left": 343, "top": 167, "text": "D", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
|
||||
"d-nig": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 206, "width": 9, "height": 9, "fillColor": "#3fb9b0", "strokeColor": "#3fb9b0", "corners": [2, 2, 2, 2]},
|
||||
"n-nig": {"type": "Text", "parent": "panel", "left": 74, "top": 202, "text": "nigel", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-nig": {"type": "Text", "parent": "panel", "left": 170, "top": 203, "text": "thinking · 32%", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-nig": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 200, "width": 22, "height": 18, "fillColor": "#1d2e22", "strokeColor": "#5cb87a", "corners": [3, 3, 3, 3]},
|
||||
"pt-nig": {"type": "Text", "parent": "panel", "left": 343, "top": 201, "text": "A", "fontColor": "#5cb87a", "fontSize": 12},
|
||||
|
||||
"perm-note": {"type": "Text", "parent": "panel", "left": 58, "top": 232, "text": "click the mode badge to cycle (D→A→P) · shift-click for bypass", "fontColor": "#5b6470", "fontSize": 10},
|
||||
|
||||
"div1": {"type": "Line", "parent": "panel", "left": 58, "top": 256, "width": 304, "height": 1, "strokeColor": "#2a3038"},
|
||||
|
||||
"h-tasks": {"type": "Text", "parent": "panel", "left": 58, "top": 268, "text": "TASKS", "fontColor": "#7a8290", "fontSize": 12},
|
||||
"t1": {"type": "Text", "parent": "panel", "left": 58, "top": 294, "text": "◐ tile-type taxonomy", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"t1a": {"type": "Text", "parent": "panel", "left": 330, "top": 294, "text": "tyre", "fontColor": "#b08cff", "fontSize": 11},
|
||||
"t2": {"type": "Text", "parent": "panel", "left": 58, "top": 318, "text": "○ reverse requirements", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"t2a": {"type": "Text", "parent": "panel", "left": 322, "top": 318, "text": "gestalt", "fontColor": "#e36fb0", "fontSize": 11},
|
||||
"t3": {"type": "Text", "parent": "panel", "left": 58, "top": 342, "text": "✓ water-model brief", "fontColor": "#7a8290", "fontSize": 12},
|
||||
|
||||
"div2": {"type": "Line", "parent": "panel", "left": 58, "top": 372, "width": 304, "height": 1, "strokeColor": "#2a3038"},
|
||||
|
||||
"h-msg": {"type": "Text", "parent": "panel", "left": 58, "top": 384, "text": "MESSAGES", "fontColor": "#7a8290", "fontSize": 12},
|
||||
"popout": {"type": "Text", "parent": "panel", "left": 300, "top": 384, "text": "⤢ pop out", "fontColor": "#5aa0e0", "fontSize": 11},
|
||||
"m1n": {"type": "Text", "parent": "panel", "left": 58, "top": 410, "text": "tyre → lead", "fontColor": "#b08cff", "fontSize": 11},
|
||||
"m1t": {"type": "Text", "parent": "panel", "left": 58, "top": 426, "text": "opening position: tiles are a discrete…", "fontColor": "#9aa4b2", "fontSize": 11},
|
||||
"m2n": {"type": "Text", "parent": "panel", "left": 58, "top": 452, "text": "lead → you", "fontColor": "#d97757", "fontSize": 11},
|
||||
"m2t": {"type": "Text", "parent": "panel", "left": 58, "top": 468, "text": "relaying gestalt's flat-taxonomy take…", "fontColor": "#9aa4b2", "fontSize": 11},
|
||||
"m3n": {"type": "Text", "parent": "panel", "left": 58, "top": 494, "text": "you → team", "fontColor": "#5aa0e0", "fontSize": 11},
|
||||
"m3t": {"type": "Text", "parent": "panel", "left": 58, "top": 510, "text": "focus on tile types only", "fontColor": "#9aa4b2", "fontSize": 11},
|
||||
|
||||
"postbox": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 700, "width": 304, "height": 36, "fillColor": "#14181d", "strokeColor": "#333340", "corners": [4, 4, 4, 4]},
|
||||
"post-t": {"type": "Text", "parent": "panel", "left": 70, "top": 710, "text": "@team message…", "fontColor": "#5b6470", "fontSize": 12}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 56 KiB |
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "Clide — team chat pane",
|
||||
"shapes": {
|
||||
"panel": {"type": "Rectangle", "left": 40, "top": 30, "width": 860, "height": 540, "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]},
|
||||
"accent": {"type": "Rectangle", "left": 40, "top": 30, "width": 860, "height": 4, "fillColor": "#5aa0e0", "strokeColor": "#5aa0e0", "corners": [8, 8, 0, 0]},
|
||||
"title": {"type": "Text", "parent": "panel", "left": 60, "top": 48, "text": "team chat", "fontColor": "#5aa0e0", "fontSize": 13},
|
||||
|
||||
"c1": {"type": "Text", "parent": "panel", "left": 60, "top": 80, "text": "tyre → lead", "fontColor": "#b08cff", "fontSize": 12},
|
||||
"m1": {"type": "Text", "parent": "panel", "left": 60, "top": 98, "text": "Opening position: a tile is a discrete surface kind on a body; propose 7 base kinds + 2 modifiers.", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
|
||||
"c2": {"type": "Text", "parent": "panel", "left": 60, "top": 134, "text": "gestalt → lead", "fontColor": "#e36fb0", "fontSize": 12},
|
||||
"m2": {"type": "Text", "parent": "panel", "left": 60, "top": 152, "text": "I'd keep it flat — a composable set bloats to hundreds of near-duplicates. One rationale per kind.", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
|
||||
"c3": {"type": "Text", "parent": "panel", "left": 60, "top": 188, "text": "you → team", "fontColor": "#5aa0e0", "fontSize": 12},
|
||||
"m3": {"type": "Text", "parent": "panel", "left": 60, "top": 206, "text": "Let's focus on tile types only this round — hold the broader derivation for later.", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
|
||||
"c4": {"type": "Text", "parent": "panel", "left": 60, "top": 242, "text": "lead → you", "fontColor": "#d97757", "fontSize": 12},
|
||||
"m4": {"type": "Text", "parent": "panel", "left": 60, "top": 260, "text": "On it — refocusing the room to a tile-type taxonomy; I'll relay each opening position as it lands.", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
|
||||
"c5": {"type": "Text", "parent": "panel", "left": 60, "top": 296, "text": "nigel → you", "fontColor": "#3fb9b0", "fontSize": 12},
|
||||
"m5": {"type": "Text", "parent": "panel", "left": 60, "top": 314, "text": "@you do types combine/compose for variety, or is a flat list enough? It changes my taxonomy.", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
|
||||
"divider": {"type": "Line", "parent": "panel", "left": 60, "top": 470, "width": 820, "height": 1, "strokeColor": "#2a3038"},
|
||||
|
||||
"to": {"type": "Rectangle", "parent": "panel", "left": 60, "top": 488, "width": 110, "height": 34, "fillColor": "#1f2a3a", "strokeColor": "#3a4250", "corners": [4, 4, 4, 4]},
|
||||
"to-t": {"type": "Text", "parent": "panel", "left": 74, "top": 496, "text": "@nigel ▾", "fontColor": "#9aa4b2", "fontSize": 13},
|
||||
"input": {"type": "Rectangle", "parent": "panel", "left": 178, "top": 488, "width": 470, "height": 34, "fillColor": "#14181d", "strokeColor": "#333340", "corners": [4, 4, 4, 4]},
|
||||
"input-t": {"type": "Text", "parent": "panel", "left": 192, "top": 496, "text": "they compose — pick from 7 base + 2 modifiers", "fontColor": "#5b6470", "fontSize": 13},
|
||||
|
||||
"intr-box": {"type": "Rectangle", "parent": "panel", "left": 660, "top": 494, "width": 16, "height": 16, "fillColor": "#14181d", "strokeColor": "#7a8290", "corners": [3, 3, 3, 3]},
|
||||
"intr-t": {"type": "Text", "parent": "panel", "left": 682, "top": 496, "text": "interrupt", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
|
||||
"send": {"type": "Rectangle", "parent": "panel", "left": 770, "top": 488, "width": 90, "height": 34, "fillColor": "#3b5bdb", "strokeColor": "#3b5bdb", "corners": [4, 4, 4, 4]},
|
||||
"send-t": {"type": "Text", "parent": "panel", "left": 794, "top": 496, "text": "Send", "fontColor": "#ffffff", "fontSize": 14}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 72 KiB |
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "Clide — sidebar Activity tab",
|
||||
"shapes": {
|
||||
"panel": {"type": "Rectangle", "left": 40, "top": 24, "width": 340, "height": 740, "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]},
|
||||
|
||||
"tab-a": {"type": "Text", "parent": "panel", "left": 58, "top": 40, "text": "Activity", "fontColor": "#5aa0e0", "fontSize": 13},
|
||||
"tab-a-u": {"type": "Rectangle", "parent": "panel", "left": 56, "top": 60, "width": 60, "height": 2, "fillColor": "#5aa0e0", "strokeColor": "#5aa0e0", "corners": [0, 0, 0, 0]},
|
||||
"tab-t": {"type": "Text", "parent": "panel", "left": 140, "top": 40, "text": "Team", "fontColor": "#7a8290", "fontSize": 13},
|
||||
"tab-c": {"type": "Text", "parent": "panel", "left": 210, "top": 40, "text": "Config", "fontColor": "#7a8290", "fontSize": 13},
|
||||
"strip": {"type": "Line", "parent": "panel", "left": 40, "top": 62, "width": 340, "height": 1, "strokeColor": "#2a3038"},
|
||||
|
||||
"h-today": {"type": "Text", "parent": "panel", "left": 58, "top": 78, "text": "TODAY", "fontColor": "#7a8290", "fontSize": 12},
|
||||
"k1": {"type": "Text", "parent": "panel", "left": 58, "top": 100, "text": "messages", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v1": {"type": "Text", "parent": "panel", "left": 200, "top": 100, "text": "142", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"k2": {"type": "Text", "parent": "panel", "left": 58, "top": 120, "text": "sessions", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v2": {"type": "Text", "parent": "panel", "left": 200, "top": 120, "text": "12", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"k3": {"type": "Text", "parent": "panel", "left": 58, "top": 140, "text": "tool calls", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v3": {"type": "Text", "parent": "panel", "left": 200, "top": 140, "text": "318", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
|
||||
"div1": {"type": "Line", "parent": "panel", "left": 58, "top": 168, "width": 304, "height": 1, "strokeColor": "#2a3038"},
|
||||
"h-life": {"type": "Text", "parent": "panel", "left": 58, "top": 182, "text": "LIFETIME", "fontColor": "#7a8290", "fontSize": 12},
|
||||
"k4": {"type": "Text", "parent": "panel", "left": 58, "top": 204, "text": "messages", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v4": {"type": "Text", "parent": "panel", "left": 200, "top": 204, "text": "4,120", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"k5": {"type": "Text", "parent": "panel", "left": 58, "top": 224, "text": "sessions", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v5": {"type": "Text", "parent": "panel", "left": 200, "top": 224, "text": "286", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
|
||||
"div2": {"type": "Line", "parent": "panel", "left": 58, "top": 252, "width": 304, "height": 1, "strokeColor": "#2a3038"},
|
||||
"h-run": {"type": "Text", "parent": "panel", "left": 58, "top": 266, "text": "RUNTIME · primary", "fontColor": "#7a8290", "fontSize": 12},
|
||||
"k6": {"type": "Text", "parent": "panel", "left": 58, "top": 288, "text": "model", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v6": {"type": "Text", "parent": "panel", "left": 200, "top": 288, "text": "claude-opus-4-7 [1M]", "fontColor": "#d97757", "fontSize": 12},
|
||||
"k7": {"type": "Text", "parent": "panel", "left": 58, "top": 308, "text": "context", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v7": {"type": "Text", "parent": "panel", "left": 200, "top": 308, "text": "54%", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"k8": {"type": "Text", "parent": "panel", "left": 58, "top": 328, "text": "mode", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v8": {"type": "Text", "parent": "panel", "left": 200, "top": 328, "text": "accept-edits", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"k9": {"type": "Text", "parent": "panel", "left": 58, "top": 348, "text": "skills", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v9": {"type": "Text", "parent": "panel", "left": 200, "top": 348, "text": "14", "fontColor": "#c8d0e0", "fontSize": 12}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "Clide — sidebar Team tab",
|
||||
"shapes": {
|
||||
"panel": {"type": "Rectangle", "left": 40, "top": 24, "width": 340, "height": 740, "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]},
|
||||
|
||||
"tab-a": {"type": "Text", "parent": "panel", "left": 58, "top": 40, "text": "Activity", "fontColor": "#7a8290", "fontSize": 13},
|
||||
"tab-t": {"type": "Text", "parent": "panel", "left": 140, "top": 40, "text": "Team", "fontColor": "#5aa0e0", "fontSize": 13},
|
||||
"tab-t-u": {"type": "Rectangle", "parent": "panel", "left": 138, "top": 60, "width": 44, "height": 2, "fillColor": "#5aa0e0", "strokeColor": "#5aa0e0", "corners": [0, 0, 0, 0]},
|
||||
"tab-c": {"type": "Text", "parent": "panel", "left": 210, "top": 40, "text": "Config", "fontColor": "#7a8290", "fontSize": 13},
|
||||
"strip": {"type": "Line", "parent": "panel", "left": 40, "top": 62, "width": 340, "height": 1, "strokeColor": "#2a3038"},
|
||||
|
||||
"d-main": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 88, "width": 9, "height": 9, "fillColor": "#d97757", "strokeColor": "#d97757", "corners": [2, 2, 2, 2]},
|
||||
"n-main": {"type": "Text", "parent": "panel", "left": 74, "top": 84, "text": "main · lead", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-main": {"type": "Text", "parent": "panel", "left": 180, "top": 85, "text": "synthesizing", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-main": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 82, "width": 22, "height": 18, "fillColor": "#1f2a44", "strokeColor": "#5aa0e0", "corners": [3, 3, 3, 3]},
|
||||
"pt-main": {"type": "Text", "parent": "panel", "left": 343, "top": 83, "text": "P", "fontColor": "#5aa0e0", "fontSize": 12},
|
||||
|
||||
"d-tyre": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 120, "width": 9, "height": 9, "fillColor": "#b08cff", "strokeColor": "#b08cff", "corners": [2, 2, 2, 2]},
|
||||
"n-tyre": {"type": "Text", "parent": "panel", "left": 74, "top": 116, "text": "tyre", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-tyre": {"type": "Text", "parent": "panel", "left": 180, "top": 117, "text": "thinking · 38%", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-tyre": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 114, "width": 22, "height": 18, "fillColor": "#1d2e22", "strokeColor": "#5cb87a", "corners": [3, 3, 3, 3]},
|
||||
"pt-tyre": {"type": "Text", "parent": "panel", "left": 343, "top": 115, "text": "A", "fontColor": "#5cb87a", "fontSize": 12},
|
||||
|
||||
"d-gest": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 152, "width": 9, "height": 9, "fillColor": "#e36fb0", "strokeColor": "#e36fb0", "corners": [2, 2, 2, 2]},
|
||||
"n-gest": {"type": "Text", "parent": "panel", "left": 74, "top": 148, "text": "gestalt", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-gest": {"type": "Text", "parent": "panel", "left": 180, "top": 149, "text": "reading · 26%", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-gest": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 146, "width": 22, "height": 18, "fillColor": "#22262e", "strokeColor": "#7a8290", "corners": [3, 3, 3, 3]},
|
||||
"pt-gest": {"type": "Text", "parent": "panel", "left": 343, "top": 147, "text": "D", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
|
||||
"d-nig": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 184, "width": 9, "height": 9, "fillColor": "#3fb9b0", "strokeColor": "#3fb9b0", "corners": [2, 2, 2, 2]},
|
||||
"n-nig": {"type": "Text", "parent": "panel", "left": 74, "top": 180, "text": "nigel", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"s-nig": {"type": "Text", "parent": "panel", "left": 180, "top": 181, "text": "thinking · 32%", "fontColor": "#7a8290", "fontSize": 11},
|
||||
"p-nig": {"type": "Rectangle", "parent": "panel", "left": 336, "top": 178, "width": 22, "height": 18, "fillColor": "#1d2e22", "strokeColor": "#5cb87a", "corners": [3, 3, 3, 3]},
|
||||
"pt-nig": {"type": "Text", "parent": "panel", "left": 343, "top": 179, "text": "A", "fontColor": "#5cb87a", "fontSize": 12},
|
||||
|
||||
"div1": {"type": "Line", "parent": "panel", "left": 58, "top": 214, "width": 304, "height": 1, "strokeColor": "#2a3038"},
|
||||
"h-tasks": {"type": "Text", "parent": "panel", "left": 58, "top": 226, "text": "TASKS", "fontColor": "#7a8290", "fontSize": 12},
|
||||
"t1": {"type": "Text", "parent": "panel", "left": 58, "top": 250, "text": "◐ tile-type taxonomy", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"t1a": {"type": "Text", "parent": "panel", "left": 330, "top": 250, "text": "tyre", "fontColor": "#b08cff", "fontSize": 11},
|
||||
"t2": {"type": "Text", "parent": "panel", "left": 58, "top": 274, "text": "○ reverse requirements", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"t2a": {"type": "Text", "parent": "panel", "left": 322, "top": 274, "text": "gestalt", "fontColor": "#e36fb0", "fontSize": 11},
|
||||
|
||||
"div2": {"type": "Line", "parent": "panel", "left": 58, "top": 306, "width": 304, "height": 1, "strokeColor": "#2a3038"},
|
||||
"h-msg": {"type": "Text", "parent": "panel", "left": 58, "top": 318, "text": "MESSAGES", "fontColor": "#7a8290", "fontSize": 12},
|
||||
"popout": {"type": "Text", "parent": "panel", "left": 300, "top": 318, "text": "⤢ pop out", "fontColor": "#5aa0e0", "fontSize": 11},
|
||||
"m1n": {"type": "Text", "parent": "panel", "left": 58, "top": 344, "text": "tyre → lead", "fontColor": "#b08cff", "fontSize": 11},
|
||||
"m1t": {"type": "Text", "parent": "panel", "left": 58, "top": 360, "text": "opening position: tiles are a discrete…", "fontColor": "#9aa4b2", "fontSize": 11},
|
||||
"m2n": {"type": "Text", "parent": "panel", "left": 58, "top": 386, "text": "lead → you", "fontColor": "#d97757", "fontSize": 11},
|
||||
"m2t": {"type": "Text", "parent": "panel", "left": 58, "top": 402, "text": "relaying gestalt's flat-taxonomy take…", "fontColor": "#9aa4b2", "fontSize": 11},
|
||||
|
||||
"postbox": {"type": "Rectangle", "parent": "panel", "left": 58, "top": 700, "width": 304, "height": 36, "fillColor": "#14181d", "strokeColor": "#333340", "corners": [4, 4, 4, 4]},
|
||||
"post-t": {"type": "Text", "parent": "panel", "left": 70, "top": 710, "text": "@team message…", "fontColor": "#5b6470", "fontSize": 12}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "Clide — sidebar Config tab",
|
||||
"shapes": {
|
||||
"panel": {"type": "Rectangle", "left": 40, "top": 24, "width": 340, "height": 740, "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]},
|
||||
|
||||
"tab-a": {"type": "Text", "parent": "panel", "left": 58, "top": 40, "text": "Activity", "fontColor": "#7a8290", "fontSize": 13},
|
||||
"tab-t": {"type": "Text", "parent": "panel", "left": 140, "top": 40, "text": "Team", "fontColor": "#7a8290", "fontSize": 13},
|
||||
"tab-c": {"type": "Text", "parent": "panel", "left": 210, "top": 40, "text": "Config", "fontColor": "#5aa0e0", "fontSize": 13},
|
||||
"tab-c-u": {"type": "Rectangle", "parent": "panel", "left": 208, "top": 60, "width": 52, "height": 2, "fillColor": "#5aa0e0", "strokeColor": "#5aa0e0", "corners": [0, 0, 0, 0]},
|
||||
"strip": {"type": "Line", "parent": "panel", "left": 40, "top": 62, "width": 340, "height": 1, "strokeColor": "#2a3038"},
|
||||
|
||||
"h-set": {"type": "Text", "parent": "panel", "left": 58, "top": 78, "text": "SETTINGS", "fontColor": "#7a8290", "fontSize": 12},
|
||||
"k1": {"type": "Text", "parent": "panel", "left": 58, "top": 100, "text": "model", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v1": {"type": "Text", "parent": "panel", "left": 200, "top": 100, "text": "opus", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"k2": {"type": "Text", "parent": "panel", "left": 58, "top": 120, "text": "output style", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v2": {"type": "Text", "parent": "panel", "left": 200, "top": 120, "text": "default", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"k3": {"type": "Text", "parent": "panel", "left": 58, "top": 140, "text": "permission mode", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v3": {"type": "Text", "parent": "panel", "left": 200, "top": 140, "text": "default", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"k4": {"type": "Text", "parent": "panel", "left": 58, "top": 160, "text": "source", "fontColor": "#9aa4b2", "fontSize": 12},
|
||||
"v4": {"type": "Text", "parent": "panel", "left": 200, "top": 160, "text": "~/.claude + .claude", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
|
||||
"div1": {"type": "Line", "parent": "panel", "left": 58, "top": 188, "width": 304, "height": 1, "strokeColor": "#2a3038"},
|
||||
|
||||
"h-skills": {"type": "Text", "parent": "panel", "left": 58, "top": 202, "text": "▸ SKILLS", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"c-skills": {"type": "Text", "parent": "panel", "left": 340, "top": 202, "text": "14", "fontColor": "#5b6470", "fontSize": 11},
|
||||
"h-agents": {"type": "Text", "parent": "panel", "left": 58, "top": 226, "text": "▸ AGENTS", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"c-agents": {"type": "Text", "parent": "panel", "left": 344, "top": 226, "text": "5", "fontColor": "#5b6470", "fontSize": 11},
|
||||
"h-cmds": {"type": "Text", "parent": "panel", "left": 58, "top": 250, "text": "▸ COMMANDS", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"c-cmds": {"type": "Text", "parent": "panel", "left": 340, "top": 250, "text": "28", "fontColor": "#5b6470", "fontSize": 11},
|
||||
"h-hooks": {"type": "Text", "parent": "panel", "left": 58, "top": 274, "text": "▸ HOOKS", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"c-hooks": {"type": "Text", "parent": "panel", "left": 344, "top": 274, "text": "3", "fontColor": "#5b6470", "fontSize": 11},
|
||||
|
||||
"h-perms": {"type": "Text", "parent": "panel", "left": 58, "top": 300, "text": "▾ PERMISSIONS", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"c-perms": {"type": "Text", "parent": "panel", "left": 340, "top": 300, "text": "9", "fontColor": "#5b6470", "fontSize": 11},
|
||||
"pa": {"type": "Text", "parent": "panel", "left": 78, "top": 322, "text": "allow", "fontColor": "#5cb87a", "fontSize": 11},
|
||||
"pa1": {"type": "Text", "parent": "panel", "left": 130, "top": 322, "text": "Bash(pql *)", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"pa2": {"type": "Text", "parent": "panel", "left": 130, "top": 340, "text": "Bash(git *)", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"pa3": {"type": "Text", "parent": "panel", "left": 130, "top": 358, "text": "Read, Glob, Grep", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"pa4": {"type": "Text", "parent": "panel", "left": 130, "top": 376, "text": "WebFetch(docs.claude.com)", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"pk": {"type": "Text", "parent": "panel", "left": 78, "top": 398, "text": "ask", "fontColor": "#e0a04a", "fontSize": 11},
|
||||
"pk1": {"type": "Text", "parent": "panel", "left": 130, "top": 398, "text": "Write, Edit", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"pk2": {"type": "Text", "parent": "panel", "left": 130, "top": 416, "text": "Bash", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"pd": {"type": "Text", "parent": "panel", "left": 78, "top": 438, "text": "deny", "fontColor": "#d9667a", "fontSize": 11},
|
||||
"pd1": {"type": "Text", "parent": "panel", "left": 130, "top": 438, "text": "Bash(rm -rf *)", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
|
||||
"h-mcp": {"type": "Text", "parent": "panel", "left": 58, "top": 466, "text": "▸ MCP SERVERS", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"c-mcp": {"type": "Text", "parent": "panel", "left": 340, "top": 466, "text": "5", "fontColor": "#5b6470", "fontSize": 11},
|
||||
|
||||
"hint": {"type": "Text", "parent": "panel", "left": 58, "top": 706, "text": "expand a list to see all · click a skill/agent/command → opens its .md →", "fontColor": "#5b6470", "fontSize": 10}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "Claude meta activity card",
|
||||
"shapes": {
|
||||
"bg": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 20, "width": 900, "height": 700,
|
||||
"fillColor": "#14171c", "strokeColor": "#333340", "corners": [10, 10, 10, 10]
|
||||
},
|
||||
"title": {
|
||||
"type": "Text", "parent": "bg",
|
||||
"left": 60, "top": 34,
|
||||
"text": "Claude pane — meta-message activity card (folds tool-call/result spam; sticky messages break the run)",
|
||||
"fontColor": "#c8d0e0", "fontSize": 14
|
||||
},
|
||||
|
||||
"labelA": {
|
||||
"type": "Text", "parent": "bg",
|
||||
"left": 60, "top": 70, "text": "A · COLLAPSED (default)", "fontColor": "#6a7280", "fontSize": 12
|
||||
},
|
||||
"proseA_bar": { "type": "Rectangle", "parent": "bg", "left": 60, "top": 94, "width": 3, "height": 46, "fillColor": "#d08447", "strokeColor": "#d08447" },
|
||||
"proseA_role": { "type": "Text", "parent": "bg", "left": 74, "top": 92, "text": "claude", "fontColor": "#d08447", "fontSize": 11 },
|
||||
"proseA_text": { "type": "Text", "parent": "bg", "left": 74, "top": 110, "text": "Let me persist the plan state (T-220 closed), then start T-221.", "fontColor": "#c8d0e0", "fontSize": 13 },
|
||||
|
||||
"cardA": { "type": "Rectangle", "parent": "bg", "left": 60, "top": 150, "width": 820, "height": 40, "fillColor": "#1a1e24", "strokeColor": "#3a4150", "corners": [6, 6, 6, 6] },
|
||||
"cardA_chev": { "type": "Text", "parent": "cardA", "left": 74, "top": 161, "text": "▸", "fontColor": "#6a7280", "fontSize": 14 },
|
||||
"cardA_dot": { "type": "Ellipse", "parent": "cardA", "left": 96, "top": 165, "width": 8, "height": 8, "fillColor": "#c8d8f0", "strokeColor": "#c8d8f0" },
|
||||
"cardA_ticker": { "type": "Text", "parent": "cardA", "left": 116, "top": 162, "text": "Bash git commit -F /tmp/clide-t220-msg.txt", "fontColor": "#aeb6c2", "fontSize": 12 },
|
||||
"cardA_badge": { "type": "Rectangle", "parent": "cardA", "left": 790, "top": 159, "width": 76, "height": 22, "fillColor": "#2a3040", "strokeColor": "#3a4150", "corners": [11, 11, 11, 11] },
|
||||
"cardA_badge_t": { "type": "Text", "parent": "cardA_badge", "left": 802, "top": 162, "text": "14 steps", "fontColor": "#c8d0e0", "fontSize": 11 },
|
||||
|
||||
"userA_bar": { "type": "Rectangle", "parent": "bg", "left": 60, "top": 208, "width": 3, "height": 40, "fillColor": "#00a3d2", "strokeColor": "#00a3d2" },
|
||||
"userA_role": { "type": "Text", "parent": "bg", "left": 74, "top": 206, "text": "you", "fontColor": "#00a3d2", "fontSize": 11 },
|
||||
"userA_text": { "type": "Text", "parent": "bg", "left": 74, "top": 224, "text": "file a ticket for the meta-message clustering", "fontColor": "#c8d0e0", "fontSize": 13 },
|
||||
"noteA": { "type": "Text", "parent": "bg", "left": 60, "top": 260, "text": "↑ a sticky message (you / claude prose) seals the card and a new cluster starts below it", "fontColor": "#6a7280", "fontSize": 11 },
|
||||
|
||||
"labelB": { "type": "Text", "parent": "bg", "left": 60, "top": 300, "text": "B · EXPANDED (click ▾)", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"proseB_bar": { "type": "Rectangle", "parent": "bg", "left": 60, "top": 324, "width": 3, "height": 46, "fillColor": "#d08447", "strokeColor": "#d08447" },
|
||||
"proseB_role": { "type": "Text", "parent": "bg", "left": 74, "top": 322, "text": "claude", "fontColor": "#d08447", "fontSize": 11 },
|
||||
"proseB_text": { "type": "Text", "parent": "bg", "left": 74, "top": 340, "text": "Let me persist the plan state (T-220 closed), then start T-221.", "fontColor": "#c8d0e0", "fontSize": 13 },
|
||||
|
||||
"cardB": { "type": "Rectangle", "parent": "bg", "left": 60, "top": 382, "width": 820, "height": 150, "fillColor": "#1a1e24", "strokeColor": "#3a4150", "corners": [6, 6, 6, 6] },
|
||||
"cardB_chev": { "type": "Text", "parent": "cardB", "left": 74, "top": 392, "text": "▾", "fontColor": "#6a7280", "fontSize": 14 },
|
||||
"cardB_hdr": { "type": "Text", "parent": "cardB", "left": 96, "top": 393, "text": "activity · 14 steps", "fontColor": "#c8d0e0", "fontSize": 12 },
|
||||
"cardB_badge_t": { "type": "Text", "parent": "cardB", "left": 812, "top": 393, "text": "▴ collapse", "fontColor": "#6a7280", "fontSize": 11 },
|
||||
"cardB_div": { "type": "Rectangle", "parent": "cardB", "left": 74, "top": 420, "width": 792, "height": 1, "fillColor": "#333340", "strokeColor": "#333340" },
|
||||
"r1": { "type": "Text", "parent": "cardB", "left": 90, "top": 432, "text": "▸ Bash pql ticket append T-220 \"Resolved (2026-06-03)…\"", "fontColor": "#aeb6c2", "fontSize": 12 },
|
||||
"r2": { "type": "Text", "parent": "cardB", "left": 90, "top": 458, "text": " Bash · result {\"id\":\"T-220\",\"type\":\"task\"…}", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"r3": { "type": "Text", "parent": "cardB", "left": 90, "top": 484, "text": "▸ Bash pql ticket status T-220 done", "fontColor": "#aeb6c2", "fontSize": 12 },
|
||||
"r4": { "type": "Text", "parent": "cardB", "left": 90, "top": 510, "text": " Bash · result {\"id\":\"T-220\"…}", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
|
||||
"errBar": { "type": "Rectangle", "parent": "bg", "left": 60, "top": 548, "width": 3, "height": 38, "fillColor": "#f06c6f", "strokeColor": "#f06c6f" },
|
||||
"errRole": { "type": "Text", "parent": "bg", "left": 74, "top": 546, "text": "error", "fontColor": "#f06c6f", "fontSize": 11 },
|
||||
"errText": { "type": "Text", "parent": "bg", "left": 74, "top": 564, "text": "Bash · error git commit → rows_written:0 (surfaced — breaks the cluster)", "fontColor": "#f06c6f", "fontSize": 12 },
|
||||
|
||||
"cardB2": { "type": "Rectangle", "parent": "bg", "left": 60, "top": 596, "width": 820, "height": 40, "fillColor": "#1a1e24", "strokeColor": "#3a4150", "corners": [6, 6, 6, 6] },
|
||||
"cardB2_chev": { "type": "Text", "parent": "cardB2", "left": 74, "top": 607, "text": "▸", "fontColor": "#6a7280", "fontSize": 14 },
|
||||
"cardB2_dot": { "type": "Ellipse", "parent": "cardB2", "left": 96, "top": 611, "width": 8, "height": 8, "fillColor": "#c8d8f0", "strokeColor": "#c8d8f0" },
|
||||
"cardB2_ticker": { "type": "Text", "parent": "cardB2", "left": 116, "top": 608, "text": "Bash rm -f /tmp/clide-t220-msg.txt", "fontColor": "#aeb6c2", "fontSize": 12 },
|
||||
"cardB2_badge": { "type": "Rectangle", "parent": "cardB2", "left": 798, "top": 605, "width": 68, "height": 22, "fillColor": "#2a3040", "strokeColor": "#3a4150", "corners": [11, 11, 11, 11] },
|
||||
"cardB2_badge_t": { "type": "Text", "parent": "cardB2_badge", "left": 810, "top": 608, "text": "2 steps", "fontColor": "#c8d0e0", "fontSize": 11 },
|
||||
|
||||
"noteB": { "type": "Text", "parent": "bg", "left": 60, "top": 648, "text": "↑ a failed result surfaces as its own row and starts a fresh card", "fontColor": "#6a7280", "fontSize": 11 }
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 130 KiB |
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"name": "T-275 Permission-mode composer control",
|
||||
"shapes": {
|
||||
"title": {"type": "Text", "left": 40, "top": 24, "text": "T-275 · Permission-mode control beside the Claude composer", "fontColor": "#c8d0e0", "fontSize": 20},
|
||||
|
||||
"secA": {"type": "Text", "left": 40, "top": 66, "text": "RESTING — icon-only button trailing the composer (label on hover / status bar / open menu)", "fontColor": "#7d8694", "fontSize": 13},
|
||||
|
||||
"a1": {"type": "Rectangle", "left": 40, "top": 96, "width": 600, "height": 72, "fillColor": "#1b1f26", "strokeColor": "#2c323c", "corners": [6, 6, 6, 6]},
|
||||
"a1hint": {"type": "Text", "left": 58, "top": 110, "text": "Message Claude…", "fontColor": "#7d8694", "fontSize": 14},
|
||||
"a1sub": {"type": "Text", "left": 58, "top": 134, "text": "Enter to send · Shift+Enter newline", "fontColor": "#525a64", "fontSize": 11},
|
||||
"a1btn": {"type": "Rectangle", "left": 596, "top": 124, "width": 32, "height": 32, "fillColor": "#20252e", "strokeColor": "#3a414d", "corners": [4, 4, 4, 4]},
|
||||
"a1ic": {"type": "Ellipse", "left": 604, "top": 132, "width": 16, "height": 16, "fillColor": "#7d8694", "strokeColor": "#7d8694"},
|
||||
|
||||
"a2": {"type": "Rectangle", "left": 40, "top": 196, "width": 600, "height": 72, "fillColor": "#1b1f26", "strokeColor": "#2c323c", "corners": [6, 6, 6, 6]},
|
||||
"a2hint": {"type": "Text", "left": 58, "top": 210, "text": "Message Claude…", "fontColor": "#7d8694", "fontSize": 14},
|
||||
"a2sub": {"type": "Text", "left": 58, "top": 234, "text": "Enter to send · Shift+Enter newline", "fontColor": "#525a64", "fontSize": 11},
|
||||
"a2btn": {"type": "Rectangle", "left": 596, "top": 224, "width": 32, "height": 32, "fillColor": "#20252e", "strokeColor": "#d8a657", "corners": [4, 4, 4, 4]},
|
||||
"a2ic": {"type": "Ellipse", "left": 604, "top": 232, "width": 16, "height": 16, "fillColor": "#d8a657", "strokeColor": "#d8a657"},
|
||||
|
||||
"a3": {"type": "Rectangle", "left": 40, "top": 296, "width": 600, "height": 72, "fillColor": "#1b1f26", "strokeColor": "#2c323c", "corners": [6, 6, 6, 6]},
|
||||
"a3hint": {"type": "Text", "left": 58, "top": 310, "text": "Message Claude…", "fontColor": "#7d8694", "fontSize": 14},
|
||||
"a3sub": {"type": "Text", "left": 58, "top": 334, "text": "Enter to send · Shift+Enter newline", "fontColor": "#525a64", "fontSize": 11},
|
||||
"a3btn": {"type": "Rectangle", "left": 596, "top": 324, "width": 32, "height": 32, "fillColor": "#20252e", "strokeColor": "#7fb0c8", "corners": [4, 4, 4, 4]},
|
||||
"a3ic": {"type": "Ellipse", "left": 604, "top": 332, "width": 16, "height": 16, "fillColor": "#7fb0c8", "strokeColor": "#7fb0c8"},
|
||||
|
||||
"secD": {"type": "Text", "left": 40, "top": 392, "text": "ACTIVE (turn running) — existing Stop row above; mode icon still trails the text box", "fontColor": "#7d8694", "fontSize": 13},
|
||||
"d1": {"type": "Rectangle", "left": 40, "top": 420, "width": 600, "height": 110, "fillColor": "#1b1f26", "strokeColor": "#2c323c", "corners": [6, 6, 6, 6]},
|
||||
"d1run": {"type": "Text", "left": 58, "top": 434, "text": "Pondering…", "fontColor": "#d97757", "fontSize": 13},
|
||||
"d1stop": {"type": "Rectangle", "left": 516, "top": 430, "width": 104, "height": 28, "fillColor": "#2a2320", "strokeColor": "#d97757", "corners": [4, 4, 4, 4]},
|
||||
"d1stopt": {"type": "Text", "left": 534, "top": 436, "text": "Stop esc", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"d1div": {"type": "Rectangle", "left": 56, "top": 466, "width": 568, "height": 1, "fillColor": "#23282f", "strokeColor": "#23282f"},
|
||||
"d1hint": {"type": "Text", "left": 58, "top": 480, "text": "Message Claude…", "fontColor": "#7d8694", "fontSize": 14},
|
||||
"d1sub": {"type": "Text", "left": 58, "top": 504, "text": "Enter to send · Shift+Enter newline", "fontColor": "#525a64", "fontSize": 11},
|
||||
"d1btn": {"type": "Rectangle", "left": 596, "top": 490, "width": 32, "height": 32, "fillColor": "#20252e", "strokeColor": "#3a414d", "corners": [4, 4, 4, 4]},
|
||||
"d1ic": {"type": "Ellipse", "left": 604, "top": 498, "width": 16, "height": 16, "fillColor": "#7d8694", "strokeColor": "#7d8694"},
|
||||
|
||||
"secB": {"type": "Text", "left": 700, "top": 66, "text": "OPEN — dropdown on click (labels live here; active marked; bypass guarded)", "fontColor": "#7d8694", "fontSize": 13},
|
||||
|
||||
"pop": {"type": "Rectangle", "left": 1118, "top": 140, "width": 200, "height": 182, "fillColor": "#1b1f26", "strokeColor": "#3a414d", "corners": [6, 6, 6, 6]},
|
||||
"r1bg": {"type": "Rectangle", "left": 1124, "top": 147, "width": 188, "height": 34, "fillColor": "#232b34", "strokeColor": "#232b34", "corners": [4, 4, 4, 4]},
|
||||
"r1ic": {"type": "Ellipse", "left": 1134, "top": 157, "width": 14, "height": 14, "fillColor": "#7d8694", "strokeColor": "#7d8694"},
|
||||
"r1lbl": {"type": "Text", "left": 1156, "top": 155, "text": "default", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"r1chk": {"type": "Text", "left": 1292, "top": 154, "text": "ok", "fontColor": "#7fb0c8", "fontSize": 12},
|
||||
"r2ic": {"type": "Ellipse", "left": 1134, "top": 193, "width": 14, "height": 14, "fillColor": "#d8a657", "strokeColor": "#d8a657"},
|
||||
"r2lbl": {"type": "Text", "left": 1156, "top": 191, "text": "accept-edits", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"r3ic": {"type": "Ellipse", "left": 1134, "top": 229, "width": 14, "height": 14, "fillColor": "#7fb0c8", "strokeColor": "#7fb0c8"},
|
||||
"r3lbl": {"type": "Text", "left": 1156, "top": 227, "text": "plan", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"rdiv": {"type": "Rectangle", "left": 1124, "top": 260, "width": 188, "height": 1, "fillColor": "#2c323c", "strokeColor": "#2c323c"},
|
||||
"r4ic": {"type": "Ellipse", "left": 1134, "top": 273, "width": 14, "height": 14, "fillColor": "#6b4f54", "strokeColor": "#6b4f54"},
|
||||
"r4lbl": {"type": "Text", "left": 1156, "top": 269, "text": "bypass", "fontColor": "#7a6166", "fontSize": 13},
|
||||
"r4note": {"type": "Text", "left": 1156, "top": 288, "text": "[lock] needs confirm", "fontColor": "#6b5358", "fontSize": 10},
|
||||
|
||||
"b1": {"type": "Rectangle", "left": 700, "top": 300, "width": 600, "height": 72, "fillColor": "#1b1f26", "strokeColor": "#2c323c", "corners": [6, 6, 6, 6]},
|
||||
"b1hint": {"type": "Text", "left": 718, "top": 314, "text": "Message Claude…", "fontColor": "#7d8694", "fontSize": 14},
|
||||
"b1sub": {"type": "Text", "left": 718, "top": 338, "text": "Enter to send · Shift+Enter newline", "fontColor": "#525a64", "fontSize": 11},
|
||||
"b1btn": {"type": "Rectangle", "left": 1256, "top": 328, "width": 32, "height": 32, "fillColor": "#232b34", "strokeColor": "#7fb0c8", "corners": [4, 4, 4, 4]},
|
||||
"b1ic": {"type": "Ellipse", "left": 1264, "top": 336, "width": 16, "height": 16, "fillColor": "#7d8694", "strokeColor": "#7d8694"},
|
||||
|
||||
"secC": {"type": "Text", "left": 700, "top": 408, "text": "INTERACTION ZONE (D-78) — a prompt replaces the composer", "fontColor": "#7d8694", "fontSize": 13},
|
||||
"c1": {"type": "Rectangle", "left": 700, "top": 438, "width": 600, "height": 96, "fillColor": "#20252e", "strokeColor": "#d8a657", "corners": [6, 6, 6, 6]},
|
||||
"c1q": {"type": "Text", "left": 718, "top": 452, "text": "Allow Edit lib/main.dart ?", "fontColor": "#c8d0e0", "fontSize": 14},
|
||||
"c1allow": {"type": "Rectangle", "left": 718, "top": 488, "width": 90, "height": 30, "fillColor": "#27332b", "strokeColor": "#7fae7f", "corners": [4, 4, 4, 4]},
|
||||
"c1allowt": {"type": "Text", "left": 740, "top": 495, "text": "Allow", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"c1deny": {"type": "Rectangle", "left": 818, "top": 488, "width": 90, "height": 30, "fillColor": "#33272b", "strokeColor": "#c87f7f", "corners": [4, 4, 4, 4]},
|
||||
"c1denyt": {"type": "Text", "left": 842, "top": 495, "text": "Deny", "fontColor": "#c8d0e0", "fontSize": 13},
|
||||
"c1note": {"type": "Text", "left": 930, "top": 462, "text": "Composer + its icon button are replaced here.", "fontColor": "#7d8694", "fontSize": 11},
|
||||
"c1note2": {"type": "Text", "left": 930, "top": 480, "text": "Mode stays readable in the status bar below.", "fontColor": "#7d8694", "fontSize": 11},
|
||||
|
||||
"sb": {"type": "Rectangle", "left": 700, "top": 548, "width": 600, "height": 26, "fillColor": "#14171c", "strokeColor": "#2c323c"},
|
||||
"sbt": {"type": "Text", "left": 716, "top": 553, "text": "opus 4.8 ·", "fontColor": "#7d8694", "fontSize": 12},
|
||||
"sbbadget": {"type": "Text", "left": 800, "top": 553, "text": "default", "fontColor": "#7d8694", "fontSize": 12},
|
||||
"sbt2": {"type": "Text", "left": 858, "top": 553, "text": "· 21k ctx · $0.12", "fontColor": "#7d8694", "fontSize": 12},
|
||||
"sbnote": {"type": "Text", "left": 700, "top": 580, "text": "passive text indicator — coloured to match the active mode, no click (default shown in neutral grey)", "fontColor": "#525a64", "fontSize": 10},
|
||||
|
||||
"nhdr": {"type": "Text", "left": 40, "top": 600, "text": "DESIGN NOTES", "fontColor": "#7d8694", "fontSize": 13},
|
||||
"n1": {"type": "Text", "left": 40, "top": 624, "text": "1. Resting control is ICON-ONLY (a compact square, per-mode coloured glyph) trailing the composer — the slot where Stop appears when busy.", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"n2": {"type": "Text", "left": 40, "top": 646, "text": "2. The mode LABEL lives in three places, never on the resting button: the hover tooltip, the status-bar badge, and the open dropdown rows.", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"n3": {"type": "Text", "left": 40, "top": 668, "text": "3. Per-mode colour: default (neutral grey), accept-edits (amber), plan (blue). bypass (red) shows only when active; it is divided off + guarded (T-181).", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"n4": {"type": "Text", "left": 40, "top": 690, "text": "4. D-78: while a permission / AskUserQuestion prompt replaces the composer, the icon button rides away with it; the status-bar badge stays as the mirror.", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"n5": {"type": "Text", "left": 40, "top": 712, "text": "5. Status-bar mode is now a passive, colour-coded TEXT indicator (no click-to-cycle). Switching lives in the composer control + Ctrl/Cmd+M (T-226).", "fontColor": "#c8d0e0", "fontSize": 12},
|
||||
"n6": {"type": "Text", "left": 40, "top": 734, "text": "6. While a turn runs, the existing Stop row (RunningIndicator + Stop) sits above the text box; the mode icon stays trailing the text box below — both visible, no conflict.", "fontColor": "#c8d0e0", "fontSize": 12}
|
||||
},
|
||||
"connectors": {
|
||||
"pop-anchor": {"tailId": "pop", "headId": "b1btn", "strokeColor": "#3a414d"}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 188 KiB |
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"name": "Pane Collapse Toggles",
|
||||
"shapes": {
|
||||
"titleA": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 50,
|
||||
"text": "STATE A — both panes open: toggles fixed at the far ends of the bottom status bar",
|
||||
"fontColor": "#c8d8f0", "fontSize": 15
|
||||
},
|
||||
"a-sidebar": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 86, "width": 200, "height": 200,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [6, 6, 6, 6]
|
||||
},
|
||||
"a-sidebar-lbl": {
|
||||
"type": "Text", "parent": "a-sidebar",
|
||||
"left": 60, "top": 100, "text": "Sidebar\n(ticket list)",
|
||||
"fontColor": "#c8d0e0", "fontSize": 13
|
||||
},
|
||||
"a-center": {
|
||||
"type": "Rectangle",
|
||||
"left": 250, "top": 86, "width": 480, "height": 200,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [6, 6, 6, 6]
|
||||
},
|
||||
"a-center-lbl": {
|
||||
"type": "Text", "parent": "a-center",
|
||||
"left": 270, "top": 100, "text": "Claude conversation (center)",
|
||||
"fontColor": "#c8d0e0", "fontSize": 13
|
||||
},
|
||||
"a-context": {
|
||||
"type": "Rectangle",
|
||||
"left": 740, "top": 86, "width": 200, "height": 200,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [6, 6, 6, 6]
|
||||
},
|
||||
"a-context-lbl": {
|
||||
"type": "Text", "parent": "a-context",
|
||||
"left": 760, "top": 100, "text": "Context\npane",
|
||||
"fontColor": "#c8d0e0", "fontSize": 13
|
||||
},
|
||||
"a-statusbar": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 292, "width": 900, "height": 28,
|
||||
"fillColor": "#15181d", "strokeColor": "#333340", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
"a-status-left": {
|
||||
"type": "Text", "parent": "a-statusbar",
|
||||
"left": 84, "top": 299, "text": "main 13 skills",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
},
|
||||
"a-status-right": {
|
||||
"type": "Text", "parent": "a-statusbar",
|
||||
"left": 770, "top": 299, "text": "Output terminal",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
},
|
||||
"a-toggle-left": {
|
||||
"type": "Rectangle",
|
||||
"left": 44, "top": 294, "width": 24, "height": 24,
|
||||
"fillColor": "#2a3040", "strokeColor": "#c8d8f0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"a-toggle-left-icon": {
|
||||
"type": "Text", "parent": "a-toggle-left",
|
||||
"left": 52, "top": 299, "text": "<", "fontColor": "#c8d8f0", "fontSize": 14
|
||||
},
|
||||
"a-toggle-right": {
|
||||
"type": "Rectangle",
|
||||
"left": 912, "top": 294, "width": 24, "height": 24,
|
||||
"fillColor": "#2a3040", "strokeColor": "#c8d8f0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"a-toggle-right-icon": {
|
||||
"type": "Text", "parent": "a-toggle-right",
|
||||
"left": 920, "top": 299, "text": ">", "fontColor": "#c8d8f0", "fontSize": 14
|
||||
},
|
||||
"a-note-left": {
|
||||
"type": "Text",
|
||||
"left": 44, "top": 328, "text": "far-left -> collapse sidebar",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
},
|
||||
"a-note-right": {
|
||||
"type": "Text",
|
||||
"left": 740, "top": 328, "text": "far-right -> collapse context",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
},
|
||||
|
||||
"titleB": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 388,
|
||||
"text": "STATE B — both collapsed: toggles stay put at the same status-bar ends, only the chevron flips",
|
||||
"fontColor": "#c8d8f0", "fontSize": 15
|
||||
},
|
||||
"b-center": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 424, "width": 900, "height": 200,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [6, 6, 6, 6]
|
||||
},
|
||||
"b-center-lbl": {
|
||||
"type": "Text", "parent": "b-center",
|
||||
"left": 380, "top": 438, "text": "Claude conversation — full width",
|
||||
"fontColor": "#c8d0e0", "fontSize": 13
|
||||
},
|
||||
"b-statusbar": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 630, "width": 900, "height": 28,
|
||||
"fillColor": "#15181d", "strokeColor": "#333340", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
"b-status-left": {
|
||||
"type": "Text", "parent": "b-statusbar",
|
||||
"left": 84, "top": 637, "text": "main 13 skills",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
},
|
||||
"b-status-right": {
|
||||
"type": "Text", "parent": "b-statusbar",
|
||||
"left": 770, "top": 637, "text": "Output terminal",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
},
|
||||
"b-toggle-left": {
|
||||
"type": "Rectangle",
|
||||
"left": 44, "top": 632, "width": 24, "height": 24,
|
||||
"fillColor": "#2a3040", "strokeColor": "#c8d8f0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"b-toggle-left-icon": {
|
||||
"type": "Text", "parent": "b-toggle-left",
|
||||
"left": 52, "top": 637, "text": ">", "fontColor": "#c8d8f0", "fontSize": 14
|
||||
},
|
||||
"b-toggle-right": {
|
||||
"type": "Rectangle",
|
||||
"left": 912, "top": 632, "width": 24, "height": 24,
|
||||
"fillColor": "#2a3040", "strokeColor": "#c8d8f0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"b-toggle-right-icon": {
|
||||
"type": "Text", "parent": "b-toggle-right",
|
||||
"left": 920, "top": 637, "text": "<", "fontColor": "#c8d8f0", "fontSize": 14
|
||||
},
|
||||
"b-note-left": {
|
||||
"type": "Text",
|
||||
"left": 44, "top": 666, "text": "same spot -> re-open sidebar",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
},
|
||||
"b-note-right": {
|
||||
"type": "Text",
|
||||
"left": 730, "top": 666, "text": "same spot -> re-open context pane",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
},
|
||||
"footer": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 700,
|
||||
"text": "Toggles sit at fixed far-left / far-right ends of the bottom status bar in every state — muscle memory, always where the reference arrows pointed. Only the chevron flips per isCollapsed. Mirrored for parity. Buttons call the existing sidebar.collapse / context.collapse commands.",
|
||||
"fontColor": "#8a92a6", "fontSize": 11
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 94 KiB |
@@ -0,0 +1,243 @@
|
||||
{
|
||||
"name": "T-54 Output Log Dock",
|
||||
"shapes": {
|
||||
"title": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 8,
|
||||
"text": "T-54 — Output / Log dock · click the status bar to toggle (open state shown)",
|
||||
"fontColor": "#c8d0e0", "fontSize": 16
|
||||
},
|
||||
|
||||
"win": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 40, "width": 1280, "height": 800,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8]
|
||||
},
|
||||
"chrome": {
|
||||
"type": "Rectangle", "parent": "win",
|
||||
"left": 40, "top": 40, "width": 1280, "height": 24,
|
||||
"fillColor": "#21262f", "strokeColor": "#333340", "corners": [8, 8, 0, 0]
|
||||
},
|
||||
"chrome-label": {
|
||||
"type": "Text", "parent": "chrome",
|
||||
"left": 54, "top": 45, "text": "clide · postmeridiem/clide", "fontColor": "#6a7280", "fontSize": 12
|
||||
},
|
||||
|
||||
"sidebar": {
|
||||
"type": "Rectangle", "parent": "win",
|
||||
"left": 40, "top": 64, "width": 200, "height": 500,
|
||||
"fillColor": "#21262f", "strokeColor": "#333340", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
"sidebar-label": {
|
||||
"type": "Text", "parent": "sidebar",
|
||||
"left": 54, "top": 76, "text": "tickets · files · git · pql", "fontColor": "#6a7280", "fontSize": 12
|
||||
},
|
||||
|
||||
"workspace": {
|
||||
"type": "Rectangle", "parent": "win",
|
||||
"left": 240, "top": 64, "width": 740, "height": 500,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
"ws-label": {
|
||||
"type": "Text", "parent": "workspace",
|
||||
"left": 256, "top": 76, "text": "Claude (home) — editor · diff · terminal lift above it (D-49 editor-mode)", "fontColor": "#6a7280", "fontSize": 12
|
||||
},
|
||||
"ws-msg": {
|
||||
"type": "Rectangle", "parent": "workspace",
|
||||
"left": 256, "top": 110, "width": 560, "height": 70,
|
||||
"fillColor": "#21262f", "strokeColor": "#333340", "corners": [6, 6, 6, 6]
|
||||
},
|
||||
"ws-prompt": {
|
||||
"type": "Rectangle", "parent": "workspace",
|
||||
"left": 256, "top": 500, "width": 708, "height": 44,
|
||||
"fillColor": "#2a3040", "strokeColor": "#c8d8f0", "corners": [8, 8, 8, 8]
|
||||
},
|
||||
"ws-prompt-label": {
|
||||
"type": "Text", "parent": "ws-prompt",
|
||||
"left": 270, "top": 514, "text": "Ask Claude…", "fontColor": "#6a7280", "fontSize": 13
|
||||
},
|
||||
|
||||
"context": {
|
||||
"type": "Rectangle", "parent": "win",
|
||||
"left": 980, "top": 64, "width": 340, "height": 500,
|
||||
"fillColor": "#21262f", "strokeColor": "#333340", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
"context-label": {
|
||||
"type": "Text", "parent": "context",
|
||||
"left": 994, "top": 76, "text": "context / reader", "fontColor": "#6a7280", "fontSize": 12
|
||||
},
|
||||
|
||||
"dock": {
|
||||
"type": "Rectangle", "parent": "win",
|
||||
"left": 40, "top": 564, "width": 1280, "height": 250,
|
||||
"fillColor": "#21262f", "strokeColor": "#333340", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
"dock-accent": {
|
||||
"type": "Rectangle", "parent": "dock",
|
||||
"left": 40, "top": 564, "width": 1280, "height": 2,
|
||||
"fillColor": "#c8d8f0", "strokeColor": "#c8d8f0", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
"dock-header": {
|
||||
"type": "Rectangle", "parent": "dock",
|
||||
"left": 40, "top": 566, "width": 1280, "height": 32,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
"tab-output": {
|
||||
"type": "Rectangle", "parent": "dock-header",
|
||||
"left": 52, "top": 570, "width": 78, "height": 24,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#c8d8f0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"tab-output-label": {
|
||||
"type": "Text", "parent": "tab-output",
|
||||
"left": 66, "top": 575, "text": "Output", "fontColor": "#c8d8f0", "fontSize": 13
|
||||
},
|
||||
"tab-problems": {
|
||||
"type": "Text", "parent": "dock-header",
|
||||
"left": 148, "top": 575, "text": "Problems 2", "fontColor": "#6a7280", "fontSize": 13
|
||||
},
|
||||
|
||||
"filter-box": {
|
||||
"type": "Rectangle", "parent": "dock-header",
|
||||
"left": 820, "top": 570, "width": 150, "height": 24,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"filter-label": {
|
||||
"type": "Text", "parent": "filter-box",
|
||||
"left": 830, "top": 575, "text": "Filter…", "fontColor": "#6a7280", "fontSize": 12
|
||||
},
|
||||
"source-dd": {
|
||||
"type": "Rectangle", "parent": "dock-header",
|
||||
"left": 980, "top": 570, "width": 122, "height": 24,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"source-label": {
|
||||
"type": "Text", "parent": "source-dd",
|
||||
"left": 990, "top": 575, "text": "Source: all ▾", "fontColor": "#c8d0e0", "fontSize": 12
|
||||
},
|
||||
"level-dd": {
|
||||
"type": "Rectangle", "parent": "dock-header",
|
||||
"left": 1112, "top": 570, "width": 116, "height": 24,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"level-label": {
|
||||
"type": "Text", "parent": "level-dd",
|
||||
"left": 1122, "top": 575, "text": "Level: info ▾", "fontColor": "#c8d0e0", "fontSize": 12
|
||||
},
|
||||
"clear-btn": {
|
||||
"type": "Rectangle", "parent": "dock-header",
|
||||
"left": 1238, "top": 570, "width": 70, "height": 24,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"clear-label": {
|
||||
"type": "Text", "parent": "clear-btn",
|
||||
"left": 1250, "top": 575, "text": "Clear", "fontColor": "#6a7280", "fontSize": 12
|
||||
},
|
||||
|
||||
"err-row-bg": {
|
||||
"type": "Rectangle", "parent": "dock",
|
||||
"left": 42, "top": 690, "width": 1276, "height": 46,
|
||||
"fillColor": "#2a1f22", "strokeColor": "#2a1f22", "corners": [0, 0, 0, 0]
|
||||
},
|
||||
|
||||
"l1-t": { "type": "Text", "parent": "dock", "left": 56, "top": 612, "text": "10:42:03.114", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l1-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 612, "text": "INFO", "fontColor": "#7f8a9c", "fontSize": 12 },
|
||||
"l1-src": { "type": "Text", "parent": "dock", "left": 222, "top": 612, "text": "ipc", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l1-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 612, "text": "IPC server listening at $XDG_RUNTIME_DIR/clide/8f3a…sock", "fontColor": "#c8d0e0", "fontSize": 12 },
|
||||
|
||||
"l2-t": { "type": "Text", "parent": "dock", "left": 56, "top": 638, "text": "10:42:03.140", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l2-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 638, "text": "INFO", "fontColor": "#7f8a9c", "fontSize": 12 },
|
||||
"l2-src": { "type": "Text", "parent": "dock", "left": 222, "top": 638, "text": "mcp", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l2-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 638, "text": "MCP/SSE listening at http://127.0.0.1:54310", "fontColor": "#c8d0e0", "fontSize": 12 },
|
||||
|
||||
"l3-t": { "type": "Text", "parent": "dock", "left": 56, "top": 664, "text": "10:42:04.880", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l3-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 664, "text": "DEBUG", "fontColor": "#5a6270", "fontSize": 12 },
|
||||
"l3-src": { "type": "Text", "parent": "dock", "left": 222, "top": 664, "text": "pane", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l3-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 664, "text": "spawned pane p1 (tmux clide:main)", "fontColor": "#9aa3b2", "fontSize": 12 },
|
||||
|
||||
"l4-t": { "type": "Text", "parent": "dock", "left": 56, "top": 696, "text": "10:42:06.330", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l4-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 696, "text": "ERROR", "fontColor": "#f06c6f", "fontSize": 12 },
|
||||
"l4-src": { "type": "Text", "parent": "dock", "left": 222, "top": 696, "text": "extensions", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l4-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 696, "text": "builtin.foo failed to activate: MissingDep('lua')", "fontColor": "#f0a6a8", "fontSize": 12 },
|
||||
"l4-stack": { "type": "Text", "parent": "dock", "left": 320, "top": 716, "text": " at LuaExtension.activate (lua_host.dart:88) › expand for full trace", "fontColor": "#6a7280", "fontSize": 11 },
|
||||
|
||||
"l5-t": { "type": "Text", "parent": "dock", "left": 56, "top": 744, "text": "10:42:07.002", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l5-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 744, "text": "WARN", "fontColor": "#d08447", "fontSize": 12 },
|
||||
"l5-src": { "type": "Text", "parent": "dock", "left": 222, "top": 744, "text": "pql", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l5-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 744, "text": "decisions sync: 1 broken ref in architecture.md", "fontColor": "#e0b48c", "fontSize": 12 },
|
||||
|
||||
"l6-t": { "type": "Text", "parent": "dock", "left": 56, "top": 770, "text": "10:42:07.115", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l6-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 770, "text": "INFO", "fontColor": "#7f8a9c", "fontSize": 12 },
|
||||
"l6-src": { "type": "Text", "parent": "dock", "left": 222, "top": 770, "text": "git", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"l6-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 770, "text": "status: 3 changed, 0 staged", "fontColor": "#c8d0e0", "fontSize": 12 },
|
||||
|
||||
"follow-pill": {
|
||||
"type": "Rectangle", "parent": "dock",
|
||||
"left": 1180, "top": 776, "width": 124, "height": 24,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#00ab9a", "corners": [12, 12, 12, 12]
|
||||
},
|
||||
"follow-label": {
|
||||
"type": "Text", "parent": "follow-pill",
|
||||
"left": 1196, "top": 781, "text": "● Following", "fontColor": "#00ab9a", "fontSize": 12
|
||||
},
|
||||
|
||||
"statusbar": {
|
||||
"type": "Rectangle", "parent": "win",
|
||||
"left": 40, "top": 814, "width": 1280, "height": 26,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [0, 0, 8, 8]
|
||||
},
|
||||
"sb-branch": { "type": "Text", "parent": "statusbar", "left": 56, "top": 820, "text": "⎇ main", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"sb-toggle": {
|
||||
"type": "Rectangle", "parent": "statusbar",
|
||||
"left": 1120, "top": 816, "width": 184, "height": 22,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#c8d8f0", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"sb-toggle-label": {
|
||||
"type": "Text", "parent": "sb-toggle",
|
||||
"left": 1132, "top": 820, "text": "▼ Output ⚠ 1 ✕ 1", "fontColor": "#c8d8f0", "fontSize": 12
|
||||
},
|
||||
|
||||
"closed-title": {
|
||||
"type": "Text",
|
||||
"left": 40, "top": 872, "text": "Collapsed + log clean — same widget now reads as the health indicator (green ✓), chevron flips to ▲:", "fontColor": "#6a7280", "fontSize": 13
|
||||
},
|
||||
"closed-bar": {
|
||||
"type": "Rectangle",
|
||||
"left": 40, "top": 898, "width": 1280, "height": 26,
|
||||
"fillColor": "#2a3040", "strokeColor": "#333340", "corners": [6, 6, 6, 6]
|
||||
},
|
||||
"closed-branch": { "type": "Text", "parent": "closed-bar", "left": 56, "top": 904, "text": "⎇ main", "fontColor": "#6a7280", "fontSize": 12 },
|
||||
"closed-toggle": {
|
||||
"type": "Rectangle", "parent": "closed-bar",
|
||||
"left": 1120, "top": 900, "width": 184, "height": 22,
|
||||
"fillColor": "#1a1e24", "strokeColor": "#00ab9a", "corners": [4, 4, 4, 4]
|
||||
},
|
||||
"closed-toggle-label": {
|
||||
"type": "Text", "parent": "closed-toggle",
|
||||
"left": 1132, "top": 904, "text": "▲ Output ✓ clean", "fontColor": "#00ab9a", "fontSize": 12
|
||||
},
|
||||
|
||||
"ann-dock": {
|
||||
"type": "Text",
|
||||
"left": 1360, "top": 580,
|
||||
"text": "BOTTOM DOCK — read-only output\nOutput (logs) + Problems (diagnostics).\nResizable height; remembers last size.\nResolves Q-28: dock = logs/problems only.\nTerminal is NOT here — it's a first-class\nwork surface in the editor pane (D-49),\ntmux-backed so it never stops on swap.",
|
||||
"fontColor": "#c8d8f0", "fontSize": 13
|
||||
},
|
||||
"ann-toggle": {
|
||||
"type": "Text",
|
||||
"left": 1360, "top": 760,
|
||||
"text": "HEALTH + LOG — ONE WIDGET\nReplaces the old app-status indicator, so\nthe bar doesn't gain a segment. Green ✓\nwhen the log is clean; ⚠/✕ counts when not.\nClick (or ⌘J) toggles the dock; ▲/▼ = state.",
|
||||
"fontColor": "#c8d8f0", "fontSize": 13
|
||||
},
|
||||
"ann-follow": {
|
||||
"type": "Text",
|
||||
"left": 1360, "top": 690,
|
||||
"text": "AUTO-SCROLL\nFollows the tail; scrolling up pauses\nfollow and shows 'Jump to latest ↓'.",
|
||||
"fontColor": "#c8d0e0", "fontSize": 13
|
||||
}
|
||||
},
|
||||
"connectors": {
|
||||
"c-dock": { "tailId": "ann-dock", "headId": "dock-header", "strokeColor": "#c8d8f0" },
|
||||
"c-toggle": { "tailId": "ann-toggle", "headId": "sb-toggle", "strokeColor": "#c8d8f0" },
|
||||
"c-follow": { "tailId": "ann-follow", "headId": "follow-pill", "strokeColor": "#c8d8f0" }
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 210 KiB |
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"name": "Settings Modal",
|
||||
"shapes": {
|
||||
"backdrop": { "type": "Rectangle", "left": 0, "top": 0, "width": 1120, "height": 720, "fillColor": "#0B0D11", "strokeColor": "#0B0D11" },
|
||||
"modal": { "type": "Rectangle", "left": 140, "top": 70, "width": 840, "height": 580, "fillColor": "#21262F", "strokeColor": "#3A4150", "corners": [8, 8, 8, 8] },
|
||||
|
||||
"title": { "type": "Text", "parent": "modal", "left": 164, "top": 90, "text": "Settings", "fontColor": "#E2E8F5", "fontSize": 18 },
|
||||
|
||||
"close": { "type": "Text", "parent": "modal", "left": 944, "top": 90, "text": "✕", "fontColor": "#6A7280", "fontSize": 16 },
|
||||
|
||||
"header-rule": { "type": "Line", "parent": "modal", "left": 140, "top": 134, "width": 840, "height": 1, "strokeColor": "#393E48" },
|
||||
|
||||
"sidebar": { "type": "Rectangle", "parent": "modal", "left": 140, "top": 135, "width": 220, "height": 467, "fillColor": "#1C2128", "strokeColor": "#1C2128" },
|
||||
"sidebar-rule": { "type": "Line", "parent": "modal", "left": 360, "top": 135, "width": 1, "height": 467, "strokeColor": "#393E48" },
|
||||
|
||||
"search": { "type": "Rectangle", "parent": "sidebar", "left": 156, "top": 150, "width": 188, "height": 30, "fillColor": "#14181E", "strokeColor": "#393E48", "corners": [6, 6, 6, 6] },
|
||||
"search-t": { "type": "Text", "parent": "search", "left": 168, "top": 158, "text": "Search settings…", "fontColor": "#5A626E", "fontSize": 12 },
|
||||
|
||||
"cat-appearance": { "type": "Text", "parent": "sidebar", "left": 172, "top": 204, "text": "Appearance", "fontColor": "#9DA5B4", "fontSize": 13 },
|
||||
"cat-editor-bg": { "type": "Rectangle", "parent": "sidebar", "left": 152, "top": 232, "width": 196, "height": 32, "fillColor": "#2A3340", "strokeColor": "#2A3340", "corners": [6, 6, 6, 6] },
|
||||
"cat-editor": { "type": "Text", "parent": "cat-editor-bg", "left": 172, "top": 240, "text": "Editor", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"cat-keymap": { "type": "Text", "parent": "sidebar", "left": 172, "top": 280, "text": "Keymap", "fontColor": "#9DA5B4", "fontSize": 13 },
|
||||
"cat-files": { "type": "Text", "parent": "sidebar", "left": 172, "top": 316, "text": "Files", "fontColor": "#9DA5B4", "fontSize": 13 },
|
||||
"cat-claude": { "type": "Text", "parent": "sidebar", "left": 172, "top": 352, "text": "Claude", "fontColor": "#9DA5B4", "fontSize": 13 },
|
||||
"cat-advanced": { "type": "Text", "parent": "sidebar", "left": 172, "top": 388, "text": "Advanced", "fontColor": "#9DA5B4", "fontSize": 13 },
|
||||
|
||||
"panel-heading": { "type": "Text", "parent": "modal", "left": 384, "top": 148, "text": "Editor", "fontColor": "#E2E8F5", "fontSize": 16 },
|
||||
"panel-legend": { "type": "Text", "parent": "modal", "left": 384, "top": 174, "text": "Edits save to this Project by default. Each field's tag is its scope — tap it to make it Always (all clide) or reset. Overridden layers show shadowed inline.", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"panel-rule": { "type": "Line", "parent": "modal", "left": 384, "top": 198, "width": 572, "height": 1, "strokeColor": "#30363F" },
|
||||
|
||||
"f1-label": { "type": "Text", "parent": "modal", "left": 384, "top": 216, "text": "Tab width", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f1-help": { "type": "Text", "parent": "modal", "left": 384, "top": 236, "text": "Spaces per indent level.", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"f1-num": { "type": "Rectangle", "parent": "modal", "left": 824, "top": 214, "width": 70, "height": 28, "fillColor": "#14181E", "strokeColor": "#393E48", "corners": [5, 5, 5, 5] },
|
||||
"f1-num-t": { "type": "Text", "parent": "f1-num", "left": 836, "top": 221, "text": "2", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f1-num-step": { "type": "Text", "parent": "f1-num", "left": 874, "top": 220, "text": "⌃⌄", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"f1-tag": { "type": "Rectangle", "parent": "modal", "left": 902, "top": 219, "width": 54, "height": 18, "fillColor": "#0E2A26", "strokeColor": "#1A5", "corners": [9, 9, 9, 9] },
|
||||
"f1-tag-t": { "type": "Text", "parent": "f1-tag", "left": 909, "top": 222, "text": "Project", "fontColor": "#33C6B4", "fontSize": 10 },
|
||||
"f1-tag-c": { "type": "Text", "parent": "f1-tag", "left": 947, "top": 222, "text": "▾", "fontColor": "#33C6B4", "fontSize": 9 },
|
||||
"f1-rule": { "type": "Line", "parent": "modal", "left": 384, "top": 262, "width": 572, "height": 1, "strokeColor": "#262B33" },
|
||||
|
||||
"f2-label": { "type": "Text", "parent": "modal", "left": 384, "top": 278, "text": "End of line", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f2-help": { "type": "Text", "parent": "modal", "left": 384, "top": 298, "text": "Line-ending written on save.", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"f2-sel": { "type": "Rectangle", "parent": "modal", "left": 784, "top": 276, "width": 110, "height": 28, "fillColor": "#14181E", "strokeColor": "#393E48", "corners": [5, 5, 5, 5] },
|
||||
"f2-sel-t": { "type": "Text", "parent": "f2-sel", "left": 796, "top": 283, "text": "LF", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f2-sel-c": { "type": "Text", "parent": "f2-sel", "left": 874, "top": 283, "text": "▾", "fontColor": "#6A7280", "fontSize": 12 },
|
||||
"f2-tag": { "type": "Rectangle", "parent": "modal", "left": 902, "top": 281, "width": 54, "height": 18, "fillColor": "#262B33", "strokeColor": "#30363F", "corners": [9, 9, 9, 9] },
|
||||
"f2-tag-t": { "type": "Text", "parent": "f2-tag", "left": 912, "top": 284, "text": "Default", "fontColor": "#6A7280", "fontSize": 10 },
|
||||
"f2-rule": { "type": "Line", "parent": "modal", "left": 384, "top": 324, "width": 572, "height": 1, "strokeColor": "#262B33" },
|
||||
|
||||
"f3-label": { "type": "Text", "parent": "modal", "left": 384, "top": 340, "text": "Insert final newline", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f3-help": { "type": "Text", "parent": "modal", "left": 384, "top": 360, "text": "Ensure files end with a newline on save.", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"f3-tog": { "type": "Rectangle", "parent": "modal", "left": 858, "top": 342, "width": 36, "height": 20, "fillColor": "#00A3D2", "strokeColor": "#00A3D2", "corners": [10, 10, 10, 10] },
|
||||
"f3-knob": { "type": "Ellipse", "parent": "f3-tog", "left": 876, "top": 344, "width": 16, "height": 16, "fillColor": "#0B0D11", "strokeColor": "#0B0D11" },
|
||||
"f3-tag": { "type": "Rectangle", "parent": "modal", "left": 902, "top": 343, "width": 54, "height": 18, "fillColor": "#0E2A26", "strokeColor": "#1A5", "corners": [9, 9, 9, 9] },
|
||||
"f3-tag-t": { "type": "Text", "parent": "f3-tag", "left": 909, "top": 346, "text": "Project", "fontColor": "#33C6B4", "fontSize": 10 },
|
||||
"f3-rule": { "type": "Line", "parent": "modal", "left": 384, "top": 388, "width": 572, "height": 1, "strokeColor": "#262B33" },
|
||||
|
||||
"f4-label": { "type": "Text", "parent": "modal", "left": 384, "top": 404, "text": "Trim trailing whitespace", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f4-help": { "type": "Text", "parent": "modal", "left": 384, "top": 424, "text": "Strip trailing spaces from each line on save.", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"f4-tog": { "type": "Rectangle", "parent": "modal", "left": 858, "top": 406, "width": 36, "height": 20, "fillColor": "#00A3D2", "strokeColor": "#00A3D2", "corners": [10, 10, 10, 10] },
|
||||
"f4-knob": { "type": "Ellipse", "parent": "f4-tog", "left": 876, "top": 408, "width": 16, "height": 16, "fillColor": "#0B0D11", "strokeColor": "#0B0D11" },
|
||||
"f4-tag": { "type": "Rectangle", "parent": "modal", "left": 896, "top": 407, "width": 60, "height": 18, "fillColor": "#2A2410", "strokeColor": "#6E5A1C", "corners": [9, 9, 9, 9] },
|
||||
"f4-tag-t": { "type": "Text", "parent": "f4-tag", "left": 906, "top": 410, "text": "Always", "fontColor": "#E0A458", "fontSize": 10 },
|
||||
"f4-rule": { "type": "Line", "parent": "modal", "left": 384, "top": 452, "width": 572, "height": 1, "strokeColor": "#262B33" },
|
||||
|
||||
"f5-label": { "type": "Text", "parent": "modal", "left": 384, "top": 468, "text": "Max line length", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f5-help": { "type": "Text", "parent": "modal", "left": 384, "top": 488, "text": "Column for the editor ruler. 0 disables it.", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"f5-num": { "type": "Rectangle", "parent": "modal", "left": 824, "top": 466, "width": 70, "height": 28, "fillColor": "#14181E", "strokeColor": "#00AB9A", "corners": [5, 5, 5, 5] },
|
||||
"f5-num-t": { "type": "Text", "parent": "f5-num", "left": 834, "top": 473, "text": "100", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f5-num-step": { "type": "Text", "parent": "f5-num", "left": 874, "top": 472, "text": "⌃⌄", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"f5-tag": { "type": "Rectangle", "parent": "modal", "left": 902, "top": 471, "width": 54, "height": 18, "fillColor": "#0E2A26", "strokeColor": "#1A5", "corners": [9, 9, 9, 9] },
|
||||
"f5-tag-t": { "type": "Text", "parent": "f5-tag", "left": 910, "top": 474, "text": "Project", "fontColor": "#33C6B4", "fontSize": 10 },
|
||||
"f5-shadow": { "type": "Text", "parent": "modal", "left": 384, "top": 506, "text": "↳ overrides Always 80 · Default 0", "fontColor": "#5A626E", "fontSize": 11 },
|
||||
"f5-rule": { "type": "Line", "parent": "modal", "left": 384, "top": 528, "width": 572, "height": 1, "strokeColor": "#262B33" },
|
||||
|
||||
"f6-label": { "type": "Text", "parent": "modal", "left": 384, "top": 544, "text": "Project .editorconfig", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
"f6-help": { "type": "Text", "parent": "modal", "left": 384, "top": 564, "text": "Per-file rules layered on top of these defaults (T-290).", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"f6-btn": { "type": "Rectangle", "parent": "modal", "left": 824, "top": 542, "width": 132, "height": 28, "fillColor": "#1A1E24", "strokeColor": "#3A4150", "corners": [5, 5, 5, 5] },
|
||||
"f6-btn-t": { "type": "Text", "parent": "f6-btn", "left": 838, "top": 549, "text": "Edit file ↗", "fontColor": "#9DA5B4", "fontSize": 12 },
|
||||
|
||||
"footer-rule": { "type": "Line", "parent": "modal", "left": 140, "top": 602, "width": 840, "height": 1, "strokeColor": "#393E48" },
|
||||
"footer-hint": { "type": "Text", "parent": "modal", "left": 384, "top": 620, "text": "Changes apply immediately. Project ▸ Always ▸ Default precedence; tags: Project teal · Always amber · Default grey.", "fontColor": "#6A7280", "fontSize": 11 },
|
||||
"footer-done": { "type": "Rectangle", "parent": "modal", "left": 884, "top": 614, "width": 72, "height": 28, "fillColor": "#2A3340", "strokeColor": "#3A4150", "corners": [5, 5, 5, 5] },
|
||||
"footer-done-t": { "type": "Text", "parent": "footer-done", "left": 908, "top": 621, "text": "Done", "fontColor": "#E2E8F5", "fontSize": 13 },
|
||||
|
||||
"menu": { "type": "Rectangle", "left": 800, "top": 242, "width": 166, "height": 84, "fillColor": "#1A1E24", "strokeColor": "#3A4150", "corners": [6, 6, 6, 6] },
|
||||
"menu-r1-bg": { "type": "Rectangle", "parent": "menu", "left": 804, "top": 246, "width": 158, "height": 22, "fillColor": "#0E2A26", "strokeColor": "#0E2A26", "corners": [4, 4, 4, 4] },
|
||||
"menu-r1": { "type": "Text", "parent": "menu-r1-bg", "left": 812, "top": 251, "text": "✓ This project", "fontColor": "#33C6B4", "fontSize": 11 },
|
||||
"menu-r2": { "type": "Text", "parent": "menu", "left": 812, "top": 277, "text": "Always — all clide", "fontColor": "#E0A458", "fontSize": 11 },
|
||||
"menu-div": { "type": "Line", "parent": "menu", "left": 806, "top": 300, "width": 154, "height": 1, "strokeColor": "#30363F" },
|
||||
"menu-r3": { "type": "Text", "parent": "menu", "left": 812, "top": 307, "text": "Reset to default", "fontColor": "#9DA5B4", "fontSize": 11 },
|
||||
|
||||
"note": { "type": "Text", "left": 140, "top": 662, "text": "Settings modal (T-302) — no header toggle: scope lives in the per-line tag. Edits default to PROJECT (.clide/settings.yaml). Tap a tag to flip it to ALWAYS (global, all clide) or Reset. Effective value + source tag always shown; overridden layers shadowed inline. Precedence Project ▸ Always ▸ Default.", "fontColor": "#5A626E", "fontSize": 11 }
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 145 KiB |
@@ -0,0 +1,254 @@
|
||||
{
|
||||
"name": "Ticket Panel — Type Filters",
|
||||
"shapes": {
|
||||
"panel": {
|
||||
"type": "Rectangle",
|
||||
"left": 80, "top": 60, "width": 400, "height": 520,
|
||||
"fillColor": "#0d0d11",
|
||||
"strokeColor": "#23232b",
|
||||
"corners": [6, 6, 6, 6]
|
||||
},
|
||||
|
||||
"filter-box": {
|
||||
"type": "Rectangle",
|
||||
"parent": "panel",
|
||||
"left": 96, "top": 76, "width": 368, "height": 30,
|
||||
"fillColor": "#16161c",
|
||||
"strokeColor": "#2c2c36",
|
||||
"corners": [5, 5, 5, 5]
|
||||
},
|
||||
"filter-icon": {
|
||||
"type": "Text",
|
||||
"parent": "filter-box",
|
||||
"left": 108, "top": 84,
|
||||
"text": "⌕",
|
||||
"fontColor": "#5b5b66",
|
||||
"fontSize": 13
|
||||
},
|
||||
"filter-text": {
|
||||
"type": "Text",
|
||||
"parent": "filter-box",
|
||||
"left": 126, "top": 85,
|
||||
"text": "Filter tickets…",
|
||||
"fontColor": "#5b5b66",
|
||||
"fontSize": 12
|
||||
},
|
||||
"refresh-icon": {
|
||||
"type": "Text",
|
||||
"parent": "filter-box",
|
||||
"left": 442, "top": 84,
|
||||
"text": "↻",
|
||||
"fontColor": "#5b5b66",
|
||||
"fontSize": 13
|
||||
},
|
||||
|
||||
"chip-init": {
|
||||
"type": "Rectangle",
|
||||
"parent": "panel",
|
||||
"left": 96, "top": 118, "width": 86, "height": 24,
|
||||
"fillColor": "#1a151f",
|
||||
"strokeColor": "#C792EA",
|
||||
"corners": [4, 4, 4, 4]
|
||||
},
|
||||
"chip-init-dot": {
|
||||
"type": "Ellipse",
|
||||
"parent": "chip-init",
|
||||
"left": 104, "top": 126, "width": 8, "height": 8,
|
||||
"fillColor": "#C792EA",
|
||||
"strokeColor": "#C792EA"
|
||||
},
|
||||
"chip-init-label": {
|
||||
"type": "Text",
|
||||
"parent": "chip-init",
|
||||
"left": 117, "top": 124,
|
||||
"text": "Initiative",
|
||||
"fontColor": "#e8e8ee",
|
||||
"fontSize": 11
|
||||
},
|
||||
|
||||
"chip-epic": {
|
||||
"type": "Rectangle",
|
||||
"parent": "panel",
|
||||
"left": 188, "top": 118, "width": 58, "height": 24,
|
||||
"fillColor": "#14171f",
|
||||
"strokeColor": "#78A0F8",
|
||||
"corners": [4, 4, 4, 4]
|
||||
},
|
||||
"chip-epic-dot": {
|
||||
"type": "Ellipse",
|
||||
"parent": "chip-epic",
|
||||
"left": 196, "top": 126, "width": 8, "height": 8,
|
||||
"fillColor": "#78A0F8",
|
||||
"strokeColor": "#78A0F8"
|
||||
},
|
||||
"chip-epic-label": {
|
||||
"type": "Text",
|
||||
"parent": "chip-epic",
|
||||
"left": 209, "top": 124,
|
||||
"text": "Epic",
|
||||
"fontColor": "#e8e8ee",
|
||||
"fontSize": 11
|
||||
},
|
||||
|
||||
"chip-story": {
|
||||
"type": "Rectangle",
|
||||
"parent": "panel",
|
||||
"left": 252, "top": 118, "width": 62, "height": 24,
|
||||
"fillColor": "#15191d",
|
||||
"strokeColor": "#7DD3A8",
|
||||
"corners": [4, 4, 4, 4]
|
||||
},
|
||||
"chip-story-dot": {
|
||||
"type": "Ellipse",
|
||||
"parent": "chip-story",
|
||||
"left": 260, "top": 126, "width": 8, "height": 8,
|
||||
"fillColor": "#7DD3A8",
|
||||
"strokeColor": "#7DD3A8"
|
||||
},
|
||||
"chip-story-label": {
|
||||
"type": "Text",
|
||||
"parent": "chip-story",
|
||||
"left": 273, "top": 124,
|
||||
"text": "Story",
|
||||
"fontColor": "#e8e8ee",
|
||||
"fontSize": 11
|
||||
},
|
||||
|
||||
"chip-task": {
|
||||
"type": "Rectangle",
|
||||
"parent": "panel",
|
||||
"left": 320, "top": 118, "width": 56, "height": 24,
|
||||
"fillColor": "#17181a",
|
||||
"strokeColor": "#9AA0AA",
|
||||
"corners": [4, 4, 4, 4]
|
||||
},
|
||||
"chip-task-dot": {
|
||||
"type": "Ellipse",
|
||||
"parent": "chip-task",
|
||||
"left": 328, "top": 126, "width": 8, "height": 8,
|
||||
"fillColor": "#9AA0AA",
|
||||
"strokeColor": "#9AA0AA"
|
||||
},
|
||||
"chip-task-label": {
|
||||
"type": "Text",
|
||||
"parent": "chip-task",
|
||||
"left": 341, "top": 124,
|
||||
"text": "Task",
|
||||
"fontColor": "#e8e8ee",
|
||||
"fontSize": 11
|
||||
},
|
||||
|
||||
"chip-bug": {
|
||||
"type": "Rectangle",
|
||||
"parent": "panel",
|
||||
"left": 382, "top": 118, "width": 54, "height": 24,
|
||||
"fillColor": "#211519",
|
||||
"strokeColor": "#E87D7D",
|
||||
"corners": [4, 4, 4, 4]
|
||||
},
|
||||
"chip-bug-dot": {
|
||||
"type": "Ellipse",
|
||||
"parent": "chip-bug",
|
||||
"left": 390, "top": 126, "width": 8, "height": 8,
|
||||
"fillColor": "#E87D7D",
|
||||
"strokeColor": "#E87D7D"
|
||||
},
|
||||
"chip-bug-label": {
|
||||
"type": "Text",
|
||||
"parent": "chip-bug",
|
||||
"left": 403, "top": 124,
|
||||
"text": "Bug",
|
||||
"fontColor": "#e8e8ee",
|
||||
"fontSize": 11
|
||||
},
|
||||
|
||||
"divider": {
|
||||
"type": "Line",
|
||||
"parent": "panel",
|
||||
"left": 96, "top": 158, "width": 368, "height": 1,
|
||||
"strokeColor": "#23232b"
|
||||
},
|
||||
|
||||
"section-header": {
|
||||
"type": "Text",
|
||||
"parent": "panel",
|
||||
"left": 98, "top": 172,
|
||||
"text": "▾ BACKLOG · 61",
|
||||
"fontColor": "#6b6b76",
|
||||
"fontSize": 10
|
||||
},
|
||||
|
||||
"card-1": {
|
||||
"type": "Rectangle",
|
||||
"parent": "panel",
|
||||
"left": 96, "top": 192, "width": 368, "height": 56,
|
||||
"fillColor": "#0d0d11",
|
||||
"strokeColor": "#23232b",
|
||||
"corners": [4, 4, 4, 4]
|
||||
},
|
||||
"card-1-dot": {
|
||||
"type": "Ellipse",
|
||||
"parent": "card-1",
|
||||
"left": 108, "top": 204, "width": 8, "height": 8,
|
||||
"fillColor": "#C792EA",
|
||||
"strokeColor": "#C792EA"
|
||||
},
|
||||
"card-1-id": {
|
||||
"type": "Text",
|
||||
"parent": "card-1",
|
||||
"left": 122, "top": 200,
|
||||
"text": "T-8",
|
||||
"fontColor": "#e8e8ee",
|
||||
"fontSize": 11
|
||||
},
|
||||
"card-1-title": {
|
||||
"type": "Text",
|
||||
"parent": "card-1",
|
||||
"left": 108, "top": 220,
|
||||
"text": "Tier 6 — extension API, settings, theming, builds",
|
||||
"fontColor": "#b8b8c2",
|
||||
"fontSize": 11
|
||||
},
|
||||
|
||||
"card-2": {
|
||||
"type": "Rectangle",
|
||||
"parent": "panel",
|
||||
"left": 96, "top": 254, "width": 368, "height": 56,
|
||||
"fillColor": "#0d0d11",
|
||||
"strokeColor": "#23232b",
|
||||
"corners": [4, 4, 4, 4]
|
||||
},
|
||||
"card-2-dot": {
|
||||
"type": "Ellipse",
|
||||
"parent": "card-2",
|
||||
"left": 108, "top": 266, "width": 8, "height": 8,
|
||||
"fillColor": "#E87D7D",
|
||||
"strokeColor": "#E87D7D"
|
||||
},
|
||||
"card-2-id": {
|
||||
"type": "Text",
|
||||
"parent": "card-2",
|
||||
"left": 122, "top": 262,
|
||||
"text": "T-19",
|
||||
"fontColor": "#e8e8ee",
|
||||
"fontSize": 11
|
||||
},
|
||||
"card-2-title": {
|
||||
"type": "Text",
|
||||
"parent": "card-2",
|
||||
"left": 108, "top": 282,
|
||||
"text": "Filter box loses focus on refresh tick",
|
||||
"fontColor": "#b8b8c2",
|
||||
"fontSize": 11
|
||||
},
|
||||
|
||||
"caption": {
|
||||
"type": "Text",
|
||||
"parent": "panel",
|
||||
"left": 96, "top": 540,
|
||||
"text": "All five type filters ON by default — click a chip to toggle, double-click to isolate.",
|
||||
"fontColor": "#56565f",
|
||||
"fontSize": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,240 @@
|
||||
# self-analysis.md — can Claude actually work inside clide?
|
||||
|
||||
**Date:** 2026-06-02
|
||||
**Author:** Claude (Opus 4.8), run as the dogfood agent against a live clide instance
|
||||
**Method:** This is not a documentation review. clide was running while I wrote this
|
||||
(socket `~/Library/Caches/clide/05cd448c962214d7.sock`, MCP SSE on `127.0.0.1:50354`,
|
||||
daemon reports `version 2.1.0`). I probed my own environment, drove the live daemon, and
|
||||
report what actually happened — with the command transcripts as evidence.
|
||||
|
||||
The question on the table: *we're close to working together inside clide — what's missing
|
||||
from my end?* Here's the honest answer.
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
**The daemon is ready. My hands are missing.**
|
||||
|
||||
Everything the CLI-first contract (D-1, D-6) promises works end-to-end at the socket
|
||||
level — `git status`, `files`, `editor`, `pane`, exit codes, the lot. I verified it live.
|
||||
But a fresh Claude session dropped into this repo **cannot reach any of it**, because:
|
||||
|
||||
1. There is **no `clide` binary on `PATH`** — and no install path that would put one there.
|
||||
2. Nothing tells a fresh agent that clide is even running, where its socket is, or that
|
||||
the CLI exists.
|
||||
3. The live UI surfaces the user sees (the Claude pane, file tree, open files) are **not
|
||||
reflected** in the registries the CLI reads — `pane list` and `editor list` came back
|
||||
empty while clide was open and in use.
|
||||
|
||||
The first one is the blocker. The fix is roughly ten lines of Makefile. The other two are
|
||||
the difference between "the agent can issue commands" and "the agent and the user are
|
||||
actually looking at the same workspace."
|
||||
|
||||
---
|
||||
|
||||
## What works today (verified live)
|
||||
|
||||
I built the C client (`make clide-cli` — it had never been built; `native/macos-arm64/`
|
||||
did not exist) and pointed it at the running daemon:
|
||||
|
||||
```
|
||||
$ native/macos-arm64/clide ping
|
||||
{"pong":true,"ts":"2026-06-02T10:45:31Z","version":"2.1.0"} # exit 0
|
||||
|
||||
$ native/macos-arm64/clide git status
|
||||
{"branch":"main","upstream":"origin/main","ahead":0,"behind":0,
|
||||
"clean":false,"unstaged":[{"path":"governance/README.md",...}, ...]} # exit 0
|
||||
|
||||
$ native/macos-arm64/clide files root
|
||||
{"path":"/Users/jeroenschweitzer/Projects/clide","ignorePatterns":79} # exit 0
|
||||
```
|
||||
|
||||
- **IPC transport is solid.** Unix socket, JSON envelopes, the `_argv` bridge — all live.
|
||||
- **The command surface is real and broad.** `pane`, `files`, `editor`, `git`, `search`,
|
||||
`pql`, `panel` subsystems all dispatch. `git status` returned my actual working tree.
|
||||
- **Exit-code discipline is correct** (this matters for an agent — it's how I know if a
|
||||
command worked):
|
||||
|
||||
| command | exit |
|
||||
|---|---|
|
||||
| `clide ping` | `0` |
|
||||
| `clide git status` | `0` |
|
||||
| `clide editor open /nonexistent/path` | `1` |
|
||||
| `clide status` (not a real command) | `3` |
|
||||
|
||||
0/1/3 map cleanly onto the pql contract. Good. An agent can trust these.
|
||||
|
||||
So the foundation is genuinely there. The gaps below are about **delivery and
|
||||
observability**, not the core design.
|
||||
|
||||
---
|
||||
|
||||
## Gap 1 — `clide` is not on PATH, and nothing installs it there *(blocker)*
|
||||
|
||||
This is the one that stops us cold.
|
||||
|
||||
```
|
||||
$ which clide → clide not found
|
||||
$ clide ping → command not found (exit 127)
|
||||
```
|
||||
|
||||
The CLI-first contract assumes I run `clide …` from Bash. I can't. Digging in:
|
||||
|
||||
- The C client (`native/clide-cli/clide.c`) is the real CLI. It builds only via the
|
||||
**separate, non-default** target `make clide-cli`, and the output had never been built.
|
||||
- `make install` on **macOS** copies *only* the `.app` bundle to `~/Applications`. It
|
||||
**never places a `clide` CLI on PATH.**
|
||||
- `make install` on **Linux** symlinks `clide` → `$(INSTALL_PREFIX)/clide/clide`, which is
|
||||
the **GUI app binary** (the Flutter runner), *not* the C client. So even the Linux path
|
||||
doesn't deliver the shell client.
|
||||
|
||||
Net: there is **no supported way** for the `clide` command to exist on an agent's PATH.
|
||||
The contract that the entire agent-IDE relationship rests on has no delivery mechanism.
|
||||
|
||||
**Impact:** Total. Without this, "Claude works inside clide via the CLI" is aspirational.
|
||||
I only got there by reverse-engineering the socket path and compiling a C file myself.
|
||||
|
||||
**Fix (small):**
|
||||
- Make `clide-cli` a dependency of `build`/`install`.
|
||||
- On macOS `install`, also drop the built C client somewhere on PATH
|
||||
(`~/.local/bin/clide`, or `/usr/local/bin`), and have the GUI launch offer to install it
|
||||
(à la VS Code's "Install 'code' command in PATH").
|
||||
- Confirm the Linux symlink targets the **C client**, not the GUI binary.
|
||||
|
||||
---
|
||||
|
||||
## Gap 2 — No bootstrap: a fresh agent doesn't know clide is there
|
||||
|
||||
Even with the binary installed, a new session has no signal that it's hosted by clide.
|
||||
There's a discovery file for the *MCP* path (`~/.claude/ide/<pid>.lock`, which correctly
|
||||
pointed at workspace + SSE URL), but **nothing for the Bash/CLI path**:
|
||||
|
||||
- No `CLIDE_SOCK` / `CLIDE_WORKSPACE` env var in my shell.
|
||||
- No injected note (CLAUDE.md fragment, system reminder) saying "you're inside clide; use
|
||||
`clide …` to drive the editor, git panel, and file tree."
|
||||
- No pre-seeded `Bash(clide *)` allow rule mentioned anywhere the agent would see it.
|
||||
|
||||
I had to be *told* "you're running inside clide" and then go find the socket. That's not
|
||||
discoverable.
|
||||
|
||||
**Impact:** High. Discovery is the difference between a capability existing and a
|
||||
capability getting used. I won't reach for `clide editor open` if I don't know it's wired
|
||||
up.
|
||||
|
||||
**Fix:** When clide spawns/hosts an agent, export `CLIDE_SOCK`/`CLIDE_WORKSPACE`, ensure
|
||||
`clide` is on the child's PATH, and inject a short context note describing the CLI surface
|
||||
and the parity contract.
|
||||
|
||||
---
|
||||
|
||||
## Gap 3 — The live UI isn't visible to the CLI *(parity premise breaks here)*
|
||||
|
||||
D-6's promise is two-way: every UI affordance has a CLI verb, **and the agent can observe
|
||||
what the user is doing**. Right now I can't see the user's surfaces. While clide was open
|
||||
and you were talking to me through it:
|
||||
|
||||
```
|
||||
$ clide pane list → {"panes":[]}
|
||||
$ clide editor active → {"active":null}
|
||||
$ clide editor list → {"buffers":[]}
|
||||
```
|
||||
|
||||
Empty. Either the built-in UI panes (the Claude conversation pane, the file tree, any open
|
||||
viewer) **don't register into the daemon registries** the CLI reads, or those registries
|
||||
only track CLI-spawned entities. Either way, the consequence is the same: **I cannot tell
|
||||
what file you're looking at, what's selected, or what panes are open.** The "agent sees
|
||||
what the user sees" half of parity isn't there yet.
|
||||
|
||||
**Impact:** High for real collaboration. Half of working *together* is me reacting to
|
||||
what's on your screen ("you've got `dispatcher.dart` open — want me to jump to the handler
|
||||
that's failing?"). Today I'm blind to it.
|
||||
|
||||
**Fix:** Make the built-in extensions register their panes/buffers/active-file state
|
||||
through the same registries the `pane`/`editor` CLI reads. Add a `clide status` umbrella
|
||||
(see Gap 6) that returns a one-shot snapshot: active pane, focused file + selection, git
|
||||
summary, layout.
|
||||
|
||||
---
|
||||
|
||||
## Gap 4 — Event observation doesn't fit how an agent runs
|
||||
|
||||
`clide tail --events` is the design's answer to "how does Claude see state change." But a
|
||||
streaming, never-returning command is awkward from a request/response tool loop — I can't
|
||||
sit on an open stream the way a long-lived UI client can. I either background it and poll a
|
||||
file, or I miss events.
|
||||
|
||||
This is more ergonomic than broken, and it overlaps the still-open Q-2 (back-pressure) and
|
||||
Q-3 (event persistence/audit). But for *me specifically* it matters.
|
||||
|
||||
**Impact:** Medium. Without a pull-based form I'll just re-run `git status` / `editor
|
||||
active` on demand and never use the event bus — which means I miss things that happen
|
||||
between my polls.
|
||||
|
||||
**Fix:** Offer a cursor-based pull alongside the stream: `clide events --since <cursor>`
|
||||
returning everything since the cursor plus a new cursor. That fits an agent loop natively
|
||||
and dovetails with Q-3's persistence question.
|
||||
|
||||
---
|
||||
|
||||
## Gap 5 — Which Claude is the dogfood agent? *(needs a decision)*
|
||||
|
||||
There's an unresolved ambiguity I bumped straight into. My shell reports
|
||||
`TERM_PROGRAM=zed` — i.e. *this* Claude (me) is an external Claude Code harness, not a
|
||||
session clide spawned via the stream-json protocol (D-77/D-78). So there are two distinct
|
||||
"Claude inside clide" stories, and they have different gaps:
|
||||
|
||||
- **(A) clide-hosted session** — clide spawns `claude --output-format stream-json`, renders
|
||||
the conversation natively, handles permission prompts as native cards (D-77/D-78). This
|
||||
is the *user's* primary Claude pane.
|
||||
- **(B) external agent driving via CLI** — a Claude Code process (like me) that issues
|
||||
`clide …` commands to manipulate the IDE.
|
||||
|
||||
These aren't the same agent. In (A), the hosted Claude *is* the conversation but would
|
||||
itself need `clide` on PATH to drive the surrounding IDE (Gaps 1–3 apply to it too). In
|
||||
(B), clide can observe my `clide …` IPC calls but is blind to the rest of my tool use
|
||||
(file reads, `make test`, plain `git`) because my shell isn't clide-owned.
|
||||
|
||||
**Impact:** Medium, but foundational — it decides what "working together inside clide"
|
||||
even means. Worth a short governance note (Q- or D-record) pinning down the intended model:
|
||||
is the dogfood agent the hosted stream-json session, an external CLI driver, or both?
|
||||
|
||||
---
|
||||
|
||||
## Gap 6 — Minor / cleanup
|
||||
|
||||
- **No `clide status` command.** There's no single one-shot "what's the whole state right
|
||||
now" call — the natural first thing an agent reaches for. (`status` currently returns
|
||||
exit 3, unknown command.) Cheap to add and high-value for orienting.
|
||||
- **MCP transport is up but not reachable by me.** The SSE server is live
|
||||
(`:50354`), but `mcp__ide__*` tools aren't in my tool list this session, and
|
||||
`getDiagnostics`/`executeCode` were noted as stubs. So neither transport (CLI nor MCP) is
|
||||
actually wired to an external agent out of the box. The CLI path is the one to fix first
|
||||
(Gap 1); MCP can follow.
|
||||
- **Version drift cosmetic check:** daemon reports `2.1.0`; worth confirming that matches
|
||||
`pubspec.yaml` so an agent keying off `clide version` isn't misled.
|
||||
|
||||
---
|
||||
|
||||
## Minimum to actually dogfool (priority order)
|
||||
|
||||
1. **Ship `clide` on PATH.** Build the C client by default; install it to a PATH dir on
|
||||
macOS *and* Linux (targeting the C client, not the GUI binary). *(Gap 1 — blocker)*
|
||||
2. **Bootstrap the agent.** Export `CLIDE_SOCK`/`CLIDE_WORKSPACE`, ensure PATH, inject a
|
||||
context note + `Bash(clide *)` allow rule when clide hosts/launches an agent. *(Gap 2)*
|
||||
3. **Make the live UI observable.** Register built-in panes/buffers into the CLI-visible
|
||||
registries; add `clide status` for a one-shot snapshot. *(Gaps 3, 6)*
|
||||
4. **Add pull-based events** (`clide events --since`). *(Gap 4)*
|
||||
5. **Decide the agent model** in governance — hosted session vs external CLI driver vs
|
||||
both. *(Gap 5)*
|
||||
|
||||
Items 1–2 are small and unblock everything. With just those, I can drive the IDE from
|
||||
Bash today — I proved the daemon answers. Item 3 is what turns "I can issue commands" into
|
||||
"we're actually working in the same workspace."
|
||||
|
||||
---
|
||||
|
||||
*Everything above was checked against a running clide, not inferred from docs. The pleasant
|
||||
surprise is how little is actually broken: the hard part (a live, correct, single-process
|
||||
IPC contract with proper exit codes) is done and working. What's missing is the last mile
|
||||
that puts the tool in the agent's hands and lets the agent see the room.*
|
||||
@@ -0,0 +1,273 @@
|
||||
# Spike: Claude Code stream-json control protocol (T-165 / T-166)
|
||||
|
||||
**Pinned to:** claude **2.1.150**. Undocumented, version-drifting internal contracts
|
||||
(per D-75/D-77) — re-validate on a CC bump, keyed off the transcript/event `version`
|
||||
field and `claude_code_version` in the `init` event.
|
||||
|
||||
**Method:** drove the real `claude` binary as a subprocess over stdin/stdout
|
||||
(`docs/spikes/` driver scripts were throwaway; the captured logs are the source of
|
||||
truth) **and** read the shipped implementation directly — the CLI is a ~238 MB node
|
||||
SEA at `~/.local/share/claude/versions/2.1.150`; `strings` on it exposes the zod
|
||||
schemas and the request/response builders. Every shape below was confirmed live
|
||||
(a real prompt round-tripped) unless marked otherwise. Cost a handful of paid turns.
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR for the implementation
|
||||
|
||||
Spawn:
|
||||
|
||||
```
|
||||
claude --input-format stream-json --output-format stream-json --verbose \
|
||||
--permission-prompt-tool stdio [ --resume <id> | --session-id <id> ]
|
||||
```
|
||||
|
||||
- **`--permission-prompt-tool stdio` is mandatory** to receive permission prompts.
|
||||
Without it, any tool that resolves to "ask" is **auto-denied** (no prompt reaches
|
||||
the client) — confirmed: a `Write` came back as `permission_denials` + an
|
||||
`is_error` tool_result "you haven't granted it yet", never a control request.
|
||||
`stdio` is a hidden value (not in `--help`); the SDK uses it (`f.sdkUrl?"stdio":…`).
|
||||
- Read stdout as line-delimited JSON. Most lines are normal stream events
|
||||
(`system`/`assistant`/`user`/`result`/`rate_limit_event`); some are
|
||||
`control_request`s you **must answer** or Claude hangs.
|
||||
- Send a prompt as `{"type":"user","message":{"role":"user","content":"…"}}`.
|
||||
stream-json does **not** echo your user messages back unless
|
||||
`--replay-user-messages` — local-echo them yourself.
|
||||
|
||||
---
|
||||
|
||||
## 1. Output event stream (stdout)
|
||||
|
||||
One JSON object per line. Types seen:
|
||||
|
||||
- `system`/`subtype:"hook_started"|"hook_progress"|"hook_response"` — session hooks
|
||||
(e.g. SessionStart). Informational; ignore for rendering.
|
||||
- `system`/`subtype:"init"` — **the config goldmine.** Carries `session_id`, `cwd`,
|
||||
`model` (`claude-opus-4-7[1m]`), `permissionMode`, `tools[]`, `mcp_servers[]`,
|
||||
`slash_commands[]`, `skills[]`, `agents[]`, `output_style`, `claude_code_version`,
|
||||
`apiKeySource`, `plugins[]`. (This is a strong source for `ClaudeConfig` / status.)
|
||||
- `assistant` — `{message:{model,id,content:[…],usage:{…}}, uuid, session_id, request_id}`.
|
||||
**Content blocks are emitted as separate `assistant` events sharing one `message.id`**
|
||||
(e.g. a `text` block, then a `tool_use` block). Block shapes are identical to the
|
||||
transcript: `text` / `thinking` (+`signature`) / `tool_use`(`id`,`name`,`input`).
|
||||
`message.usage` carries `input_tokens` + `cache_read_input_tokens` +
|
||||
`cache_creation_input_tokens` (→ context-token count).
|
||||
- `user` — two flavours: (a) **tool results** Claude received —
|
||||
`message.content:[{type:"tool_result",tool_use_id,content,is_error}]` plus a
|
||||
top-level `tool_use_result`; (b) **harness-injected user messages** — a skill
|
||||
load (`Skill` tool → text begins `"Base directory for this skill:"`), a
|
||||
slash-command expansion, or a system reminder. Injected ones carry
|
||||
**`isSynthetic: true`** at the top level (the transcript uses `isMeta`
|
||||
instead). Verified by boundary test: the inject only appears once the `Skill`
|
||||
tool is actually invoked (it's auto-allowed, no prompt); if Claude just runs a
|
||||
command inferred from the slash text, no inject is emitted. clide flags these
|
||||
(`UserMessage.injected`) and renders them as a muted, collapsed "context"
|
||||
card, not a blue "you" message.
|
||||
- `result` — terminal turn summary: `result` (final text), `usage`, `total_cost_usd`,
|
||||
`permission_denials[]`, `num_turns`, **`modelUsage.<model>.contextWindow`** (e.g.
|
||||
`1000000`) **and `maxOutputTokens`** — i.e. the context-window *size* IS exposed
|
||||
here (relevant to the T-158 budget gap; the remaining-budget % still is not).
|
||||
- `rate_limit_event` — `{rate_limit_info:{status,resetsAt,rateLimitType,…}}`.
|
||||
- `stream_event` — **only with `--include-partial-messages`** (T-184). Wraps the
|
||||
raw Anthropic streaming deltas as they arrive: `{type:"stream_event", event:{…}}`
|
||||
where `event.type` is `message_start` (carries `message.id`) → `content_block_start`
|
||||
→ `content_block_delta` (`delta:{type:"text_delta",text}` / `thinking_delta` /
|
||||
`input_json_delta`, with `index`) → `content_block_stop` → `message_delta` →
|
||||
`message_stop`. The full per-block `assistant` event still arrives interleaved.
|
||||
**Verified against 2.1.150 in BOTH `--print` and the interactive `--input-format
|
||||
stream-json` transport** — the flag's `--help` claim that it "only works with
|
||||
--print" is wrong; it streams in interactive mode too. `content_block_delta`
|
||||
events do NOT carry a message id, so track the id from the preceding
|
||||
`message_start`. (This is the real shape; the earlier guess of
|
||||
`assistant`+`partial:true` was wrong — `StreamJsonSession` streams text from
|
||||
`stream_event` and finalises via the matching `assistant` event.)
|
||||
|
||||
The existing `parseTranscriptChunk` (transcript_reader.dart) parses the
|
||||
`assistant`/`user` events as-is (same `message.content` shapes; missing
|
||||
`uuid`/`timestamp` default harmlessly). Permission mode comes from `init`, not a
|
||||
`permission-mode` record.
|
||||
|
||||
## 2. Control protocol (bidirectional, same stdin/stdout)
|
||||
|
||||
### Envelope
|
||||
Claude → client:
|
||||
```json
|
||||
{"type":"control_request","request_id":"<uuid>","request":{"subtype":"…", …}}
|
||||
```
|
||||
Client → Claude (the reply):
|
||||
```json
|
||||
{"type":"control_response","response":{"subtype":"success","request_id":"<uuid>","response":{…}}}
|
||||
```
|
||||
On failure use `{"subtype":"error","request_id":"…","error":"…"}`. **`request_id`
|
||||
must echo exactly.** An unanswered `control_request` hangs the turn.
|
||||
|
||||
### `can_use_tool` (the permission ask) — confirmed live
|
||||
Request:
|
||||
```json
|
||||
{"type":"control_request","request_id":"70c8…","request":{
|
||||
"subtype":"can_use_tool","tool_name":"Write","display_name":"Write",
|
||||
"input":{"file_path":"…","content":"…"},
|
||||
"description":"banana.txt",
|
||||
"permission_suggestions":[{"type":"setMode","mode":"acceptEdits","destination":"session"}],
|
||||
"tool_use_id":"toolu_…"}}
|
||||
```
|
||||
**ALLOW** — `updatedInput` is **required** (a bare `{"behavior":"allow"}` is rejected
|
||||
with a ZodError: *"updatedInput: expected record, received undefined"*). Echo the
|
||||
`input` back unchanged to allow as-is, or modify it to alter the call:
|
||||
```json
|
||||
{"type":"control_response","response":{"subtype":"success","request_id":"70c8…",
|
||||
"response":{"behavior":"allow","updatedInput":{ …the tool input… }}}}
|
||||
```
|
||||
**DENY** — `message` is required:
|
||||
```json
|
||||
{"type":"control_response","response":{"subtype":"success","request_id":"70c8…",
|
||||
"response":{"behavior":"deny","message":"User declined."}}}
|
||||
```
|
||||
(Decision zod union also allows optional `updatedPermissions` on allow.)
|
||||
|
||||
### AskUserQuestion — confirmed live (the non-obvious one)
|
||||
AskUserQuestion is **not** answered with a `tool_result` (that path is rejected as a
|
||||
dismissal — it even shows up in `permission_denials`). It is **permission-gated and
|
||||
answered through the same `can_use_tool` channel**. Its `input` is exactly clide's
|
||||
own AskUserQuestion shape:
|
||||
`{questions:[{question,header,multiSelect,options:[{label,description}]}]}`.
|
||||
Return the user's choice by injecting an **`answers` map into `updatedInput`**:
|
||||
`answers : record(question-text → chosen-label)` (multi-select = comma-separated labels).
|
||||
```json
|
||||
{"behavior":"allow","updatedInput":{
|
||||
"questions":[ …echoed… ],
|
||||
"answers":{"Do you prefer cats or dogs?":"Dogs"}}}
|
||||
```
|
||||
Confirmed: Claude then reported *"You chose Dogs"*; the resulting tool_use_result was
|
||||
`{questions:[…],answers:{"Do you prefer cats or dogs?":"Dogs"}}`. Leaving `answers`
|
||||
empty makes Claude say "your selection didn't come through".
|
||||
|
||||
### Other `control_request` subtypes (from the binary's zod schemas)
|
||||
Client→Claude requests it accepts: `initialize`, `interrupt`,
|
||||
`set_permission_mode {mode}`, `mcp_message {server_name,message}`.
|
||||
Claude→client requests you may receive: `can_use_tool`, `hook_callback
|
||||
{callback_id,input,tool_use_id?}`. **Answer every inbound control_request** (even
|
||||
unknown subtypes — reply `error` "Unsupported…") or the turn stalls. There is also a
|
||||
`control_cancel_request` for in-flight cancellation.
|
||||
|
||||
### `initialize` handshake — optional, but a config goldmine
|
||||
Sending it first is **not** what enables permissions (the `stdio` flag does that —
|
||||
verified: initialize-then-Write was still auto-denied). But the response is rich:
|
||||
```json
|
||||
// → {"type":"control_request","request_id":"init-1","request":{"subtype":"initialize","hooks":{},"sdkMcpServers":[]}}
|
||||
// ← response.response = {commands:[{name,description,argumentHint,aliases?}…],
|
||||
// agents:[…], models:[…], output_style, available_output_styles,
|
||||
// account:{email,organization,subscriptionType,apiProvider}, pid}
|
||||
```
|
||||
`commands[]` carries **descriptions + argumentHints** the `init` event's bare
|
||||
`slash_commands[]` lacks — better source for the typeahead (T-152) and ClaudeConfig.
|
||||
|
||||
## 3. Implications for the tickets
|
||||
- **T-165:** spawn with the flags in §0; feed `parseTranscriptChunk` from the
|
||||
`assistant`/`user` events; pull `permissionMode`/`model` from `init`; local-echo
|
||||
user sends. Isolate all protocol framing in one module (drift-containment, D-77).
|
||||
- **T-166:** wire `can_use_tool` → native prompt card (allow/deny, using
|
||||
`display_name`/`description`/`permission_suggestions`); AskUserQuestion → option
|
||||
picker, returning `updatedInput.answers`. Answer **every** control_request.
|
||||
- **ClaudeConfig (D-76):** prefer the `initialize` response's `commands[]` (has
|
||||
descriptions) over the `init` `slash_commands[]`.
|
||||
|
||||
## 4. MCP vs the control channel — which layer does what
|
||||
These are **complementary, not competing** — a recurring point of confusion:
|
||||
- **The conversation + permissions + AskUserQuestion ride the stream-json control
|
||||
channel** (`can_use_tool` over stdin/stdout). This is the SDK-blessed transport and
|
||||
what `canUseTool` maps to. T-165/T-166 use it. **MCP is not involved here.**
|
||||
- **MCP is how you give Claude extra *tools/capabilities*.** That's exactly what the
|
||||
VSCode/JetBrains extensions do: they host an MCP *server* exposing IDE features to
|
||||
Claude (`mcp__ide__getDiagnostics`, `mcp__ide__executeCode`, openDiff) — they do
|
||||
**not** route the conversation or permissions through MCP; permissions still go
|
||||
through the CLI/control channel. D-77's clide-hosted MCP broker (T-170) is the same
|
||||
idea: provide team messaging / task-sync tools to the agents.
|
||||
- **One overlap exists:** `--permission-prompt-tool` accepts *either* the special
|
||||
`stdio` value (control channel — what we use) *or* an **MCP tool name** (Claude
|
||||
calls your MCP tool to get the allow/deny). So permissions *could* be routed through
|
||||
MCP — but it's strictly more machinery than `stdio`, needs a server, and loses the
|
||||
structured `permission_suggestions`/`display_name` the control request carries.
|
||||
**Recommendation: keep permissions on `stdio`; reserve MCP for capability/tool
|
||||
provision (IDE context later, the team broker in T-170).**
|
||||
|
||||
## 5. Resilience — the stdio channel is brittle; the fallback menu (no empty slate)
|
||||
**Decision (this spike):** carry the conversation + permissions + AskUserQuestion on
|
||||
the **stdio control channel** (`--permission-prompt-tool stdio` + `can_use_tool`).
|
||||
Rationale: most direct — no extra process, no loopback "networking", and Claude hands
|
||||
us structured prompt metadata for free. **But it is an undocumented internal contract;
|
||||
Anthropic can change or remove it on any version bump.** This section exists so that
|
||||
if it shifts we start from a researched menu, not a blank page.
|
||||
|
||||
**How we'd detect a break (canaries):**
|
||||
- Pin `claude_code_version` (here **2.1.150**); diff it on every CC upgrade.
|
||||
- Symptoms of regression: tools **auto-denied despite** `--permission-prompt-tool
|
||||
stdio`; `can_use_tool` requests missing/renamed; `ZodError` tool_results rejecting
|
||||
our `control_response` (e.g. the `updatedInput`-required quirk changing).
|
||||
- The captured spike logs (this doc's source) double as **regression fixtures** — wire
|
||||
them into the parser/control-handler tests so a CC bump that breaks shapes goes red.
|
||||
|
||||
**Fallback menu, in rough preference order if `stdio` is withdrawn:**
|
||||
1. **MCP permission tool** — `--permission-prompt-tool mcp__clide__approve` against the
|
||||
clide-hosted MCP server (T-170 builds that server anyway). Claude calls our MCP
|
||||
tool to get the allow/deny. More layers, but **MCP is a stable, public protocol** —
|
||||
the strongest fallback precisely because the broker will already exist.
|
||||
2. **Permission modes** — degrade to `--permission-mode acceptEdits` / `dontAsk` /
|
||||
`bypassPermissions` for a reduced-fidelity UX (no per-call prompt) as a stopgap.
|
||||
Public, stable flags; buys time to build a better path.
|
||||
3. **Agent SDK** — if Anthropic ships/keeps a stable public `canUseTool` SDK surface
|
||||
(TS/Python), shell out to or port it. Supported, but a heavier dep + language seam.
|
||||
4. **ACP (Agent Client Protocol)** — if the ecosystem converges on ACP
|
||||
(`session/request_permission`, Zed's path) as the blessed editor-integration
|
||||
standard, adopt its adapter. Open standard; would reframe the whole transport.
|
||||
|
||||
**Containment:** all protocol framing lives behind one module (per D-77), so swapping
|
||||
transports is a one-seam change. Re-capture fixtures per pinned version.
|
||||
|
||||
## 6. Hosting an in-process MCP server over the control channel (T-170 — VERIFIED)
|
||||
Verified live against 2.1.150 (2026-05-25): clide can host an MCP server whose tools
|
||||
claude calls, **entirely over the stream-json control channel** — no subprocess, no
|
||||
`--mcp-config` file, no socket. This is the cleanest fit for the single-process
|
||||
guardrail and is what the team broker (T-170) is built on.
|
||||
|
||||
**Registration — `initialize` handshake only (no `--mcp-config` needed).** List the
|
||||
server name(s) in the `initialize` control_request's `sdkMcpServers`:
|
||||
```json
|
||||
// → {"type":"control_request","request_id":"init-1","request":{
|
||||
// "subtype":"initialize","hooks":{},"sdkMcpServers":["clide-team"]}}
|
||||
```
|
||||
A run with **no `--mcp-config` flag at all** but this handshake worked end-to-end — the
|
||||
flag is not required for SDK (in-process) servers. The server name surfaces tools to the
|
||||
model as `mcp__<server>__<tool>` (e.g. `mcp__clide-team__ping`).
|
||||
|
||||
**Handshake claude then drives (inbound `mcp_message` control_requests).** For each,
|
||||
the message is a JSON-RPC object; reply with a `control_response` carrying the JSON-RPC
|
||||
result under **`response.response.mcp_response`**:
|
||||
```json
|
||||
// ← {"type":"control_request","request_id":"<rid>","request":{
|
||||
// "subtype":"mcp_message","server_name":"clide-team",
|
||||
// "message":{"method":"initialize","params":{"protocolVersion":"2025-11-25",…},"jsonrpc":"2.0","id":0}}}
|
||||
// → {"type":"control_response","response":{"subtype":"success","request_id":"<rid>",
|
||||
// "response":{"mcp_response":{"jsonrpc":"2.0","id":0,"result":{
|
||||
// "protocolVersion":"2025-11-25","capabilities":{"tools":{"listChanged":false}},
|
||||
// "serverInfo":{"name":"clide-team","version":"0.0.1"}}}}}}
|
||||
```
|
||||
Sequence observed: `initialize` → `notifications/initialized` (no `id`; still answer it)
|
||||
→ `tools/list` → (model calls a tool) → `tools/call`. The `tools/call` message:
|
||||
`{"method":"tools/call","params":{"name":"ping","arguments":{…},"_meta":{"claudecode/toolUseId":…,"progressToken":…}},"jsonrpc":"2.0","id":2}`;
|
||||
answer with `mcp_response.result = {"content":[{"type":"text","text":…}],"isError":false}`.
|
||||
|
||||
**SDK MCP tool calls ARE permission-gated.** Before the `tools/call`, claude sends a
|
||||
normal `can_use_tool` for `mcp__clide-team__ping` (with `permission_suggestions` →
|
||||
`addRules`). So the broker's tools flow through the same allow/deny path as any tool —
|
||||
no special-casing needed; the existing `can_use_tool` handler covers them.
|
||||
|
||||
Provenance: two live capture runs (`init-strings`, no `--mcp-config`; and `mcpconfig`)
|
||||
both completed the full round-trip returning `pong-from-clide`. The raw logs aren't
|
||||
committed (the `initialize` response embeds account email/org); the contract is instead
|
||||
pinned in the transport tests (`MCP server hosting (T-170)`).
|
||||
|
||||
## Not done / open
|
||||
- `hook_callback` round-trip not exercised live (shape from the binary only).
|
||||
- Image/file paste intake over stream-json `content` blocks not tested here.
|
||||
- Remaining-usage budget % still not exposed (only `contextWindow` size, in `result`).
|
||||
@@ -0,0 +1,78 @@
|
||||
# Spike: Claude Code team / transcript internals (T-134)
|
||||
|
||||
**Pinned to:** claude **2.1.148**, tmux **3.6a**. These are undocumented, version-drifting
|
||||
internal contracts (per D-75) — re-validate on a CC bump.
|
||||
|
||||
**Method:** validated from real on-disk artifacts (42 past team `config.json`s, real
|
||||
team + Task-tool subagent transcripts, `.meta.json` written by the current version) plus
|
||||
a synthetic `tmux -L clide` control-mode test and the tmux manual. No live team run was
|
||||
needed to answer the questions — the existing artifacts are conclusive and cost no quota.
|
||||
|
||||
---
|
||||
|
||||
## Findings
|
||||
|
||||
### 1. Teammates get tmux panes; transcripts live under `subagents/`
|
||||
- **Confirmed:** across real teams, **42 teammate members carry a populated `tmuxPaneId`**
|
||||
(e.g. `%5`, `%120`); the lead's `tmuxPaneId` is `""`. So teammates spawn as panes and the
|
||||
config records the pane id.
|
||||
- **Teammate transcript location:** `~/.claude/projects/<munged-cwd>/<session-id>/subagents/agent-<hex>.jsonl`,
|
||||
with a sibling `agent-<hex>.meta.json`. `<munged-cwd> = absolutePath.replaceAll('/','-')`
|
||||
(leading `-` kept). Note: a team's `<session-id>` dir held **0 top-level `*.jsonl`** and
|
||||
**44 `subagents/agent-*.jsonl`** — teammate content is the subagent files, not top-level sessions.
|
||||
- Each subagent record carries `agentId` (the **hex**, e.g. `a2a3530` — matches the filename),
|
||||
`sessionId` (the dir), `isSidechain: true`, `slug` (a random codename), `type`
|
||||
(`user`/`assistant`/…). This is the same JSONL schema `TranscriptReader` (T-136) already parses.
|
||||
|
||||
### 2. Lifecycle signal — control mode vs. polling
|
||||
- **tmux 3.6a control-mode notifications** (from `man tmux`): `%window-add`, `%window-close`,
|
||||
`%window-pane-changed`, `%layout-change`, `%unlinked-window-add`, `%unlinked-window-close`,
|
||||
`%session-changed`, `%sessions-changed`, `%pane-mode-changed`, `%exit`, `%output`/`%extended-output`, …
|
||||
**There is NO `%pane-died`** (an-idea.md assumed one). Pane/teammate exit surfaces via
|
||||
`%window-close` / `%layout-change` / `%window-pane-changed`.
|
||||
- **Driving control mode from code is finicky:** a `tmux -L clide -C attach` captured
|
||||
`%session-changed`/`%exit` but the attach exited early under non-interactive Bash; reliably
|
||||
consuming the stream needs a long-lived managed client.
|
||||
- **Polling `tmux -L clide list-panes -a -F '...'` works and is reliable** (validated: it
|
||||
enumerated panes with pane-id/pid/title). **Recommendation for T-139: use polling as the
|
||||
baseline lifecycle source**; treat control mode as a later optimization.
|
||||
|
||||
### 3. Team config schema (`~/.claude/teams/<team>/config.json`)
|
||||
- Top keys: `name`, `description`, `createdAt`, `leadAgentId`, `leadSessionId`, `members[]`.
|
||||
- Member keys: `name`, `agentId` (=`<name>@<team>`), `agentType`, `model`, `cwd`,
|
||||
`joinedAt`, `subscriptions`, `tmuxPaneId`. (Some runs also carry `backendType`/`isActive`/`mode` —
|
||||
optional, version-varying.) The lead member has empty `tmuxPaneId`.
|
||||
|
||||
### 4. ⚠️ Identity linkage — the real risk for T-139
|
||||
The pane/teammate identity in **config** does NOT share a key with the **transcript file**:
|
||||
- Config: `{name: gestalt, agentId: gestalt@control-interaction, agentType: gestalt, tmuxPaneId: %120}`.
|
||||
- Transcript: `agent-<hex>.jsonl` (records `agentId = <hex>`, `slug = <random>`) +
|
||||
`agent-<hex>.meta.json = {agentType, description}`.
|
||||
- **The only join key is `agentType`** (config.member.agentType ↔ `.meta.json.agentType`).
|
||||
This is **unambiguous only when teammates have distinct agentTypes** (e.g. `control-interaction`:
|
||||
gestalt/ozzie/tyre/…). For **same-type teammates** (e.g. `art-requirements`: 3× `general-purpose`)
|
||||
agentType is ambiguous → need a disambiguator: spawn order / `joinedAt` timestamp vs. file mtime,
|
||||
or parse the **lead transcript's** teammate-spawn records (likely carry both ids). **T-139 must
|
||||
handle this**; recommend: join on agentType, fall back to ordering by `joinedAt`/mtime, and
|
||||
investigate the lead transcript's spawn events for an explicit hex↔name link.
|
||||
|
||||
### 5. Resolved elsewhere
|
||||
- **Session-id discovery (check 5):** Claude doesn't expose its session id; pick newest `*.jsonl`
|
||||
by mtime — already implemented in `TranscriptReader` (T-136).
|
||||
- **Paste (check 4):** scope to `@path` file references over `send-keys` (text channel). Clipboard
|
||||
image paste needs an interactive display ($DISPLAY) and is out of scope for headless validation;
|
||||
decide the composer's image handling in T-138/T-006.
|
||||
|
||||
## Recommendations for the team tickets
|
||||
- **T-139 (observer):** poll `list-panes -a` for lifecycle; read team `config.json` for the roster
|
||||
+ pane ids; tail teammate transcripts at `<munged>/<sid>/subagents/agent-<hex>.jsonl`; resolve
|
||||
pane→transcript via `agentType` join with a `joinedAt`/mtime tiebreaker (and confirm whether the
|
||||
lead transcript gives an explicit link). Isolate all of this behind the one module (D-75).
|
||||
- **Re-validate on any CC version bump** — key off the transcript `version` field.
|
||||
|
||||
## Not done
|
||||
- A **live, real-time** team run (watching a pane + transcript appear live) was not executed —
|
||||
the static artifacts answer every question and a live run costs quota without adding certainty.
|
||||
The one item a fresh run would pin precisely: whether the **lead transcript** records an explicit
|
||||
teammate hex↔name mapping (would remove the same-type ambiguity). Worth a short observed run when
|
||||
T-139 is implemented.
|
||||
@@ -8,10 +8,10 @@ on any Linux or macOS dev box without network access or shared state.
|
||||
|
||||
| layer | location | runner | time | when |
|
||||
|---|---|---|---|---|
|
||||
| unit (root) | `test/` | `dart test` | ~5s | `make test` |
|
||||
| unit + widget + golden (app) | `app/test/` | `flutter test` | ~30s | `make test` |
|
||||
| a11y contract | `app/test/a11y/` | `flutter test` | ~5s | `make test-a11y` |
|
||||
| integration (startup gate) | `app/integration_test/` | `flutter test integration_test/` | ~60s | `make test-integration` |
|
||||
| unit (Flutter-free core) | `test/ipc/`, `test/daemon/`, `test/pty/` | `dart test` | ~5s | `make test-core` |
|
||||
| unit + widget + golden | `test/` | `flutter test` | ~30s | `make test` |
|
||||
| a11y contract | `test/a11y/` | `flutter test` | ~5s | `make test-a11y` |
|
||||
| integration (startup gate) | `integration_test/` | `flutter test integration_test/` | ~60s | `make test-integration` |
|
||||
| daemon E2E + web WASM smoke | `test/daemon/` + `tools/ui/tests/` | `dart test` + Playwright | ~60s | `make test-e2e` |
|
||||
| startup bundle smoke | `ci/smoke_bundle.sh` | xvfb-run, 5s timeout | ~30s | `make smoke-bundle` |
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Claude Code needs to *actually use* the app while building features.
|
||||
The pipeline:
|
||||
|
||||
1. **Flutter builds the app to WASM.** `flutter build web --wasm` ships
|
||||
a CanvasKit/Skwasm bundle under `app/build/web/`.
|
||||
a CanvasKit/Skwasm bundle under `build/web/`.
|
||||
2. **A local server serves it.** `tools/ui/serve.sh` starts
|
||||
`http://localhost:4280` in the background with a pidfile.
|
||||
3. **Playwright drives a headless Chromium.** Instead of click-by-pixel
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# friction.md
|
||||
|
||||
A running log of install / setup / environment friction — the papercuts that
|
||||
cost time getting clide (and its tooling) working on a machine. Newest first.
|
||||
|
||||
Each entry: **symptom → root cause → fix**, with enough detail that the next
|
||||
person (or the next clone) can recognise and resolve it fast.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-06 — pql hooks disabled the repo's pre-push gate
|
||||
|
||||
**Symptom**
|
||||
- "pql setup is broken." On the surface pql worked (`pql schema`,
|
||||
`pql decisions sync`, `pql plan status` all fine), but the git hook wiring
|
||||
was in a split-brain state.
|
||||
|
||||
**Root cause**
|
||||
- The repo's intended setup is `git config core.hooksPath .githooks` (run via
|
||||
`make hooks`). `.githooks/` is the committed, canonical hook set: it carries
|
||||
**both** pql's planning delegators (pre-commit / post-merge / post-checkout /
|
||||
post-rewrite, each sourcing `.pql/hooks/*`) **and** the repo's own `pre-push`
|
||||
gate (`make push-check`).
|
||||
- `core.hooksPath` was **unset** in every scope, so git fell back to the
|
||||
default `.git/hooks/`. `pql init` had populated *that* directory with only
|
||||
the pql delegators — **no `pre-push`**.
|
||||
- Net effect: the pre-push gate (decisions + core + fast tests + a11y) was
|
||||
silently **not running** on push, while pql's automation ran only by accident
|
||||
from the default dir. CLAUDE.md requires the gate to always run and forbids
|
||||
`--no-verify`, so this was a real (silent) regression, not cosmetic.
|
||||
|
||||
**Fix**
|
||||
- `make hooks` → restores `core.hooksPath = .githooks` (now `pre-push` and the
|
||||
pql delegators all fire from one canonical, git-tracked source).
|
||||
- Removed the stray pql-only delegators from `.git/hooks/`
|
||||
(`pre-commit`, `post-merge`, `post-checkout`, `post-rewrite`). They became
|
||||
inert once hooksPath pointed elsewhere, and leaving them created a dangerous
|
||||
partial fallback — a hook set that runs pql but silently skips the pre-push
|
||||
gate if `core.hooksPath` ever gets unset again. The real hook logic lives in
|
||||
`.pql/hooks/` and was untouched.
|
||||
|
||||
**Watch out for**
|
||||
- `pql init` installs into the default `.git/hooks/` and does **not** respect an
|
||||
existing `core.hooksPath`. After running it, re-run `make hooks` and verify
|
||||
with `git rev-parse --git-path hooks` (should print `.githooks`) and confirm
|
||||
`.githooks/pre-push` is present & executable.
|
||||
@@ -111,15 +111,34 @@ You might also want, project-permitting:
|
||||
- [D-67: Pql changelog files are committed alongside code](decisions/process.md#d-67-pql-changelog-files-are-committed-alongside-code) — _process_
|
||||
- [D-68: Dual integration surface — Bash CLI primary, MCP secondary](decisions/architecture.md#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) — _architecture_
|
||||
- [D-69: published themes are user contracts; ship -hc variants for a11y](decisions/accessibility.md#d-69-published-themes-are-user-contracts-ship--hc-variants-for-a11y) — _accessibility_
|
||||
- [D-70: IPC socket path is per-workspace, deterministic](decisions/architecture.md#d-70-ipc-socket-path-is-per-workspace-deterministic) — _architecture_
|
||||
- [D-71: IPC socket access gated by chmod 0600 on socket + parent](decisions/architecture.md#d-71-ipc-socket-access-gated-by-chmod-0600-on-socket--parent) — _architecture_
|
||||
- [D-72: IPC server is multi-connection with serial dispatch on the main isolate](decisions/architecture.md#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate) — _architecture_
|
||||
- [D-73: MCP transport for /ide is SSE over HTTP](decisions/architecture.md#d-73-mcp-transport-for-ide-is-sse-over-http) — _architecture_
|
||||
- [D-74: IPC command schema is co-registered with the handler, validated at dispatch](decisions/architecture.md#d-74-ipc-command-schema-is-co-registered-with-the-handler-validated-at-dispatch) — _architecture_
|
||||
- [D-75: Claude rendered natively from transcripts; terminal retained as general tool only](decisions/architecture.md#d-75-claude-rendered-natively-from-transcripts-terminal-retained-as-general-tool-only) — _architecture_
|
||||
- [D-76: ClaudeConfig — Claude's config surface is clide's app settings (builtin-owned, watched, probe-cached per version)](decisions/architecture.md#d-76-claudeconfig--claudes-config-surface-is-clides-app-settings-builtin-owned-watched-probe-cached-per-version) — _architecture_
|
||||
- [D-77: Drive Claude via the stream-json control protocol; teams become a clide-owned coordination layer](decisions/architecture.md#d-77-drive-claude-via-the-stream-json-control-protocol-teams-become-a-clide-owned-coordination-layer) — _architecture_
|
||||
- [D-78: Claude permission/prompt transport is the stdio control channel](decisions/architecture.md#d-78-claude-permissionprompt-transport-is-the-stdio-control-channel) — _architecture_
|
||||
- [D-79: Workspace content search is a pure-Dart in-process engine, outside pql](decisions/architecture.md#d-79-workspace-content-search-is-a-pure-dart-in-process-engine-outside-pql) — _architecture_
|
||||
- [D-80: `files.read` allows trusted Claude config roots beyond the workspace](decisions/architecture.md#d-80-filesread-allows-trusted-claude-config-roots-beyond-the-workspace) — _architecture_
|
||||
- [D-81: Right-pane reader load is driven by a retained `ReaderNav`, not per-view state or bus retention](decisions/architecture.md#d-81-right-pane-reader-load-is-driven-by-a-retained-readernav-not-per-view-state-or-bus-retention) — _architecture_
|
||||
- [D-82: Keymap sequences are space-separated; matching is a reusable matcher consumed at the interception point](decisions/architecture.md#d-82-keymap-sequences-are-space-separated-matching-is-a-reusable-matcher-consumed-at-the-interception-point) — _architecture_
|
||||
- [D-83: Dogfood agent model — hosted stream-json session primary, external CLI driver secondary](decisions/architecture.md#d-83-dogfood-agent-model--hosted-stream-json-session-primary-external-cli-driver-secondary) — _architecture_
|
||||
- [D-84: Diff view placement — editor-mode inline above Claude, spawned from the git sidebar](decisions/architecture.md#d-84-diff-view-placement--editor-mode-inline-above-claude-spawned-from-the-git-sidebar) — _architecture_
|
||||
- [D-85: Event bus delivery — bounded ring-buffer back-pressure; in-memory cursor retention, bus-owned if persisted](decisions/architecture.md#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) — _architecture_
|
||||
- [D-86: MCP tool surface — full clide namespace generated from the co-registered command registry](decisions/architecture.md#d-86-mcp-tool-surface--full-clide-namespace-generated-from-the-co-registered-command-registry) — _architecture_
|
||||
- [D-87: Output/log dock — bottom, toggled, read-only (logs + problems)](decisions/architecture.md#d-87-outputlog-dock--bottom-toggled-read-only-logs--problems) — _architecture_
|
||||
- [D-88: clide-owned anchored popover + menu primitive](decisions/design.md#d-88-clide-owned-anchored-popover--menu-primitive) — _design_
|
||||
- [D-89: inline pasted-image thumbnails that expand to the lightbox](decisions/design.md#d-89-inline-pasted-image-thumbnails-that-expand-to-the-lightbox) — _design_
|
||||
- [D-90: clide:// deep links — paranoid allowlist + user confirmation](decisions/architecture.md#d-90-clide-deep-links--paranoid-allowlist--user-confirmation) — _architecture_
|
||||
- [D-91: Unified conversation drawing card backed by a canvas renderer](decisions/architecture.md#d-91-unified-conversation-drawing-card-backed-by-a-canvas-renderer) — _architecture_
|
||||
|
||||
## Open questions
|
||||
|
||||
- [Q-1: Authorisation granularity on the IPC socket](questions/architecture.md#q-1-authorisation-granularity-on-the-ipc-socket) — _architecture_
|
||||
- [Q-2: Back-pressure on event streams](questions/architecture.md#q-2-back-pressure-on-event-streams) — _architecture_
|
||||
- [Q-3: Event persistence + audit/undo](questions/architecture.md#q-3-event-persistence--auditundo) — _architecture_
|
||||
- [Q-4: `.canvas` schema compatibility with Obsidian](questions/architecture.md#q-4-canvas-schema-compatibility-with-obsidian) — _architecture_
|
||||
- [Q-5: IPC wire-format stability + `schema_version:`](questions/architecture.md#q-5-ipc-wire-format-stability--schema-version) — _architecture_
|
||||
- [Q-6: Window chrome — native frame vs frameless custom](questions/architecture.md#q-6-window-chrome--native-frame-vs-frameless-custom) — _architecture_
|
||||
- [Q-7: macOS app bundle signing / notarisation](questions/architecture.md#q-7-macos-app-bundle-signing--notarisation) — _architecture_
|
||||
- [Q-8: Extension API shape — widgets, subcommands, both?](questions/extensions.md#q-8-extension-api-shape--widgets-subcommands-both) — _extensions_
|
||||
- [Q-9: Lua runtime vendoring](questions/extensions.md#q-9-lua-runtime-vendoring) — _extensions_
|
||||
@@ -132,18 +151,25 @@ You might also want, project-permitting:
|
||||
- [Q-16: `tree-sitter-dart` grammar maintenance](questions/process.md#q-16-tree-sitter-dart-grammar-maintenance) — _process_
|
||||
- [Q-17: Icon set growth](questions/process.md#q-17-icon-set-growth) — _process_
|
||||
- [Q-18: Theme hot-reload in release builds](questions/process.md#q-18-theme-hot-reload-in-release-builds) — _process_
|
||||
- [Q-19: (withdrawn)](questions/process.md#q-19-withdrawn) — _process_
|
||||
- [Q-20: Kernel DB service — namespaced SQL access?](questions/process.md#q-20-kernel-db-service--namespaced-sql-access) — _process_
|
||||
- [Q-21: Pql absorbs planning vs keeps separate](questions/architecture.md#q-21-pql-absorbs-planning-vs-keeps-separate) — _architecture_
|
||||
- [Q-22: Ticket persistence strategy](questions/architecture.md#q-22-ticket-persistence-strategy) — _architecture_
|
||||
- [Q-23: SSH-remote development — run clide against a remote workspace](questions/architecture.md#q-23-ssh-remote-development--run-clide-against-a-remote-workspace) — _architecture_
|
||||
- [Q-25: Body text face — mono everywhere vs Josefin Sans UI + mono code](questions/architecture.md#q-25-body-text-face--mono-everywhere-vs-josefin-sans-ui--mono-code) — _architecture_
|
||||
- [Q-26: Small screen layout (< 1000px)](questions/architecture.md#q-26-small-screen-layout--1000px) — _architecture_
|
||||
- [Q-27: Two-editor split](questions/architecture.md#q-27-two-editor-split) — _architecture_
|
||||
- [Q-28: Terminal strip scope — shell only or logs/errors/tests](questions/architecture.md#q-28-terminal-strip-scope--shell-only-or-logserrorstests) — _architecture_
|
||||
- [Q-29: Branch picker location](questions/architecture.md#q-29-branch-picker-location) — _architecture_
|
||||
- [Q-30: Focus behavior when editor is dirty and viewer is peeked](questions/architecture.md#q-30-focus-behavior-when-editor-is-dirty-and-viewer-is-peeked) — _architecture_
|
||||
- [Q-31: XWayland fallback for frameless — proper Wayland protocol needed](questions/architecture.md#q-31-xwayland-fallback-for-frameless--proper-wayland-protocol-needed) — _architecture_
|
||||
- [Q-34: How + when to surface the account/team token budget given upstream doesn't expose it](questions/architecture.md#q-34-how--when-to-surface-the-accountteam-token-budget-given-upstream-doesnt-expose-it) — _architecture_
|
||||
|
||||
## Resolved questions
|
||||
|
||||
- [Q-2: Back-pressure on event streams](questions/architecture.md#q-2-back-pressure-on-event-streams) — _architecture_
|
||||
- [Q-3: Event persistence + audit/undo](questions/architecture.md#q-3-event-persistence--auditundo) — _architecture_
|
||||
- [Q-6: Window chrome — native frame vs frameless custom](questions/architecture.md#q-6-window-chrome--native-frame-vs-frameless-custom) — _architecture_
|
||||
- [Q-19: (withdrawn)](questions/process.md#q-19-withdrawn) — _process_
|
||||
- [Q-21: Pql absorbs planning vs keeps separate](questions/architecture.md#q-21-pql-absorbs-planning-vs-keeps-separate) — _architecture_
|
||||
- [Q-22: Ticket persistence strategy](questions/architecture.md#q-22-ticket-persistence-strategy) — _architecture_
|
||||
- [Q-28: Terminal strip scope — shell only or logs/errors/tests](questions/architecture.md#q-28-terminal-strip-scope--shell-only-or-logserrorstests) — _architecture_
|
||||
- [Q-32: MCP tool surface — minimum slash-ide or extended clide tools?](questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools) — _architecture_
|
||||
- [Q-33: MCP transport — SSE, WebSocket, stdio, or all?](questions/architecture.md#q-33-mcp-transport--sse-websocket-stdio-or-all) — _architecture_
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ A11y + i18n are Tier-0 contracts, not Tier-6 polish.
|
||||
### D-21: i18n is a Tier-0 contract (fframe pattern + locale-fallback chain)
|
||||
- **Date:** 2026-04-21
|
||||
- **Decision:** All user-facing strings resolve through a namespaced i18n catalogue loader ported from fframe's text-driven pattern, extended with a locale-fallback chain fframe lacks. JSON per locale; `I18n.of(context).t('namespace.key', {vars})`. Missing keys resolve down the chain (e.g. `en_GB` → `en` → default), never fail silently; missing at the base locale logs a dev-mode error.
|
||||
- **Rationale:** Flutter's `intl` + ARB codegen is inflexible for plugin-contributed catalogs (see [R-4](rejected.md#r-4-flutter-intl-and-arb-codegen)) — we need per-extension catalogs that merge without a codegen step. fframe's shape fits; its silent-fallback behaviour does not, so we add the chain.
|
||||
- **Rationale:** Flutter's `intl` + ARB codegen is inflexible for plugin-contributed catalogs (see [R-4](../rejected/accessibility.md#r-4-flutter-intl--arb-codegen-for-i18n)) — we need per-extension catalogs that merge without a codegen step. fframe's shape fits; its silent-fallback behaviour does not, so we add the chain.
|
||||
- **Cost:** JSON has no comments and no trailing commas; translation tooling has to accept that. Separate `i18n` facade on every feature.
|
||||
- **Raised by:** 2026-04-21 planning.
|
||||
|
||||
|
||||
@@ -4,11 +4,55 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
|
||||
---
|
||||
|
||||
### D-1: CLI-first, not MCP
|
||||
- **Date:** 2026-04-20 (was ADR 0001; ported from the claudian lineage)
|
||||
- **Amendment (2026-05-15):** D-1's intent — the CLI is the *primary* agent-facing surface, with the same contract as pql — stands. An additional `/ide`-compatible MCP surface is added per [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary); both wrap the same in-process dispatcher. The escape-hatch line in this record's Cost ("nothing here precludes adding [MCP] later that shells out to the same CLI") is realised — the MCP server does not bypass the CLI's surface, it offers a second transport to it.
|
||||
- **Decision:** Claude talks to clide exclusively via Bash (`clide …`). No MCP server. No protocol layer in Claude's face. The CLI uses the same exit-code + stderr-JSON contract as pql.
|
||||
- **Context:** The two mainstream options for the agent-facing surface were an MCP server or a plain Bash CLI matching pql's contract.
|
||||
- **Rationale:** Same mental model as pql for the agent — one tool-use pattern covers both. No MCP runtime to host, authenticate, or keep in sync with client versions. User/Claude parity is easier to enforce: every CLI subcommand must have a UI affordance in the Flutter app and vice versa ([D-6](#d-6-cli-and-event-surface-contract)). Claude Code's `Bash(clide *)` allow rule is the only configuration clide needs on the agent side.
|
||||
- **Cost:** If an MCP-only integration becomes compelling later (e.g. a multi-agent scenario), nothing here precludes adding one that shells out to the same CLI.
|
||||
- **Raised by:** Ported from the claudian lineage.
|
||||
|
||||
### D-3: pql as supporter tool; clide wraps, never duplicates
|
||||
- **Date:** 2026-04-20 (was ADR 0003; ported from the claudian lineage)
|
||||
- **Decision:** Two complementary rules. **(1) Wrap, don't duplicate.** Clide never re-implements backlinks, ranking, frontmatter parsing, or wikilink resolution for query purposes. If a capability is missing in pql, it is added upstream in pql's repo and clide bumps the dependency. The only place clide contains pql logic is `lib/src/pql/` — pure shell-outs to the `pql` binary. **(2) pql is a clide subsystem when clide is present in the repo.** On load, clide writes its current state into `.pql/config.yaml` — no conditional sync. Clide only stomps keys it manages (starting with `ignore_files:` — see [D-4](#d-4-ignore-file-strategy)). Other pql config keys are left alone. Clide does **not** touch pql's index/cache data under `<repo>/.pql/` — that stays pql's private store.
|
||||
- **Context:** [`pql`](https://github.com/postmeridiem/pql) is a pre-existing Go CLI that indexes a markdown-bearing directory tree into SQLite and exposes frontmatter, wikilinks, tags, headings, and bases through a query surface. Clide needs those capabilities for its Query panel, canvas drivers, graph view, and any feature that needs to know structure.
|
||||
- **Rationale:** One source of truth for markdown semantics. Any new query capability the UI wants goes through a pql upstream PR, not a local workaround. Users never have to learn pql's config file to get consistent behaviour — clide manages it. The arrow clide → pql is never inverted: pql stays ignorant of its wrapper.
|
||||
- **Cost:** Clide's `lib/src/pql/` package is deliberately thin. pql is also the **only** query engine — Obsidian-style inline "bases" are explicitly not supported; queries live at the repo level. In repos without clide, pql works standalone unaffected.
|
||||
- **Raised by:** Ported from the claudian lineage. Load-bearing for [D-39](process.md#d-39-planning-tooling-lives-in-pql-not-clide).
|
||||
|
||||
### D-4: Ignore file strategy
|
||||
- **Date:** 2026-04-20 (was ADR 0004; ported from the claudian lineage)
|
||||
- **Decision:** One mechanism everywhere: the `ignore_files:` list in `.pql/config.yaml`. Ordered list of gitignore-shaped files; later entries win on per-pattern conflicts. pql defaults to `ignore_files: [.gitignore]`. Per [D-3](#d-3-pql-as-supporter-tool-clide-wraps-never-duplicates), clide writes the list on load — `[.gitignore, .clideignore]` if `.clideignore` exists, else `[.gitignore]`. `.clideignore` carries **only** the clide-specific deviations from `.gitignore` (supports `!pattern` negations); never duplicate gitignore's contents. Walker magic: none except `.git/` — every other tool-owned dir (`.pql/`, `.clide/`) is added to `.gitignore` at install time; exclusion flows through the normal `ignore_files:` chain.
|
||||
- **Context:** Every file-enumerating surface in clide (pql query panels, canvas drivers, graph view, file watchers, pane lists, file tree) needs to skip the obvious junk — `vendor/`, `node_modules/`, `dist/`, build artifacts — or results drown in noise. Clide's working assumption is that the git repo *is* the workspace — no separate "vault" concept.
|
||||
- **Rationale:** Users get one config knob, in a file they might already know (pql users) or never need to touch (clide-only users). `.clideignore` is short by design — it's deltas, not a full list. Sidecar consumers read the same key and apply identical precedence, so Claude and the user always see the same filtered surface.
|
||||
- **Cost:** Removing clide from a repo leaves pql working with vanilla defaults (clide's last-written `ignore_files:` stays until pql or the user rewrites it; worth reconsidering during uninstall design).
|
||||
- **Raised by:** Ported from the claudian lineage.
|
||||
|
||||
### D-5: Dart core; sidecar dissolved; `ptyc` as pql-peer
|
||||
- **Date:** 2026-04-20 (was ADR 0005; supersedes [R-2](../rejected/architecture.md#r-2-go-sidecar))
|
||||
- **Amendment (2026-04-23):** The separate daemon process and two-package layout are dissolved per [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server). Dart-core principle survives; the daemon binary does not.
|
||||
- **Amendment (2026-05-07):** `ptyc` retired. PTY spawning moved to Dart FFI `forkpty()` (`lib/src/pty/native_pty.dart`). The `ptyc/` source tree, `PtySession`, and `scm_rights.dart` are removed. `pql` remains the sole external supporter tool.
|
||||
- **Amendment (2026-05-17):** `forkpty()` replaced with `posix_openpt()` + `posix_spawn()` (T-96). `forkpty` calls `fork()` underneath, which is unsafe in the multithreaded Dart VM: ~5% of spawns deadlocked in the child before `execve` due to libc locks held by ghost-threads at fork time. `posix_spawn` uses `vfork` under glibc/musl/macOS, keeping the parent suspended until `execve` completes — no Dart code runs in the child. Side benefit: dropped the `libutil.so.1` dynamic dependency; PTY now resolves entirely against libc via `DynamicLibrary.process()`.
|
||||
- **Decision:** Three moves. **(1) Dart is the core language.** Everything that used to live under `sidecar/` — IPC server, CLI dispatch, process management, file watching, git shell-outs, pql wrapper — is written in Dart. Two execution modes of one Dart AOT binary: `clide <subcommand>` (one-shot, pql-style) and `clide --daemon` (long-running, owns PTYs and subprocesses, survives app restarts). The Flutter app imports the Dart core as a library *and* connects to the daemon over IPC. **(2) The sidecar directory dissolves.** Layout is `app/` (Flutter UI), `lib/` (Dart core), `bin/clide.dart` (AOT entry), `ptyc/` (C helper), no `sidecar/`, no Go module. **(3) `ptyc` is a pql-peer supporter tool.** Small C binary that does `posix_openpt` + `fork` + `exec` + fd-passing via `SCM_RIGHTS`; clide wraps it the same way it wraps pql. Shells out for every PTY (terminal pane, tmux session, Claude, LSP server, debug adapter — one code path). Consumers other than clide can use `ptyc` standalone.
|
||||
- **Context:** [R-2](../rejected/architecture.md#r-2-go-sidecar) picked Go for the sidecar/CLI on two premises: (a) the heavy work belongs in a language separate from the UI layer, and (b) pql is Go so the muscle memory transfers. On reassessment, both premises broke: the "heavy work" is I/O-bound glue that `dart:io` covers cleanly — the real choice was **separate process vs shared language**, and separate-process is what matters. PTY is the one place Dart is genuinely weak (multi-threaded VM can't safely `fork()`), and once you accept a small native helper, *nothing else* needs to be in the same language.
|
||||
- **Rationale:** One toolchain for the IDE proper (Flutter + Dart). C toolchain needed only to build `ptyc` — tiny, rarely-changing. Session persistence stays because PTY master fds live in the Dart daemon process, not the app. `ptyc` naming: **p** for *project* (parallel to pql's *project query language*), **ptyc** reads as both "PTY + child" (domain vocabulary) and "PTY + C" (implementation language). Usable from Dart, Python, Go, shell — anywhere a subprocess can be spawned and a fd received.
|
||||
- **Cost:** Rust remains an escape hatch, not a plan. If a Dart limit later forces a second native helper (file-watching at scale on macOS, a tree-sitter host, etc.), the precedent is: new native need → new supporter tool, peer of pql and `ptyc`. Never a second "core language." Supply-chain gates stay, shape changes — Go `govulncheck` removed, Dart advisories review + exact-pin stays, `ptyc` gets a "read the 150 lines" review checklist (see `make security`).
|
||||
- **Raised by:** 2026-04-20 reassessment. See also the `ptyc` naming note in the original ADR (read as Project Terminal Controller / PTY+C / PTY+child).
|
||||
|
||||
### D-6: CLI and event surface contract
|
||||
- **Date:** 2026-04-20 (was ADR 0006)
|
||||
- **Decision:** The CLI is organised into **subsystems**. Each subsystem owns a noun, a set of verbs, and a set of events. The set is closed at any point in time (documented); growth is additive (new verbs, new events — never renaming existing ones without a version bump). Initial subsystems (by tier): `pane`, `tab`, `open`, `editor`, `panel`, `tree`, `git`, `pql`, `canvas`, `graph`, `theme`, `settings`, `project`. Two umbrella entry points sit outside any subsystem: `clide tail --events [--filter <subsystem>[:<id>]]` and `clide status`. Command shape: `clide <subsystem> <verb> [<positional>...] [--flag ...] [-- argv...]`. Exit codes parity with pql (`0/1/2/3/4` + `64-78` sysexits reserved); diagnostic JSON on **stderr** on non-zero exit; stdout stays machine-parseable on success. Events are JSON objects, one per line, with `v`, `ts`, `type` (`<subsystem>.<verb_past|noun_changed>`), `subsystem`, `id`, and `payload`; binary payloads base64. Every state-changing command emits at least one event; read-only commands emit nothing. Replay buffer per subsystem (default depth 16) so late subscribers still see recent effects. Parity rule: every UI affordance has a matching CLI verb (or a follow-up task naming the verb); every CLI verb surfaces in the UI (or documents why it's Claude-only).
|
||||
- **Context:** [D-1](#d-1-cli-first-not-mcp) established that Claude drives clide via a Bash CLI. That decided the *channel* — it did not define the *surface*. CLAUDE.md stated the rule colloquially ("every CLI subcommand has a UI affordance … if you add one side without the other, the feature is incomplete"); this record restates it as an implementable contract that satisfies user/Claude parity, daemon-as-authoritative-state, and pql-style ergonomics at once.
|
||||
- **Rationale:** Surface is enumerable — adding a subsystem means adding a row and specifying verbs + events. Wire schema is versioned (`v: 1` starting point; compatibility breaks bump the major and land alongside a `pubspec.yaml` `schema_version:` bump — see [Q-5](../questions/architecture.md#q-5-ipc-wire-format-stability--schema-version)). Events are the only UI→app state channel; the Flutter app does not poll. Extensions inherit this — a Dart extension publishes a subsystem; the same registration pipeline exposes it to Claude via the CLI.
|
||||
- **Cost:** Replay-buffer memory per subsystem (cheap — most emit seldom). Back-pressure on firehose streams ([Q-2](../questions/architecture.md#q-2-back-pressure-on-event-streams)), authorisation granularity ([Q-1](../questions/architecture.md#q-1-authorisation-granularity-on-the-ipc-socket)), and event persistence ([Q-3](../questions/architecture.md#q-3-event-persistence--auditundo)) are all deferred until Tier 1 is in real use.
|
||||
- **Raised by:** 2026-04-20 planning.
|
||||
|
||||
### D-7: App root is bare `WidgetsApp`
|
||||
- **Date:** 2026-04-21
|
||||
- **Decision:** The Flutter app root is `WidgetsApp`, not `MaterialApp` or `CupertinoApp`. Clide's look is fully custom; the Material/Cupertino shells would drag in opinionated theming, default icons, and platform chrome we'd then have to fight.
|
||||
- **Rationale:** Clide is a Linux-primary desktop IDE with a custom theme pipeline and custom primitives (panels, tabs, panes, canvas). Material's implicit theming collides with [D-9](#d-9-three-tier-theme-pipeline); Cupertino is iOS-flavoured. `WidgetsApp` gives us routing, locale, focus traversal, semantics, and Directionality without aesthetic baggage.
|
||||
- **Cost:** We build and own every primitive; no `ElevatedButton` fallback. See [R-3](rejected.md#r-3-materialapp-root) and [R-7](rejected.md#r-7-cupertinoapp-root).
|
||||
- **Cost:** We build and own every primitive; no `ElevatedButton` fallback. See [R-3](../rejected/architecture.md#r-3-materialapp-root) and [R-7](../rejected/architecture.md#r-7-cupertinoapp-root).
|
||||
- **Raised by:** 2026-04-21 planning.
|
||||
|
||||
### D-8: Feature-first folder layout
|
||||
@@ -28,7 +72,7 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
### D-10: State management — `ChangeNotifier` + `ListenableBuilder`
|
||||
- **Date:** 2026-04-21
|
||||
- **Decision:** Per-feature state uses `ChangeNotifier` exposed through a feature facade (singleton-per-kernel); widgets subscribe via `ListenableBuilder`. No Riverpod, Provider, BLoC, or Redux.
|
||||
- **Rationale:** SDK-shipped, zero deps, trivial to fake in tests (hand-rolled fakes in [D-25](testing.md#d-25-mocks-mocktail-at-io-plus-hand-rolled-fakes)). Violates [D-31 prefer-zero-deps](tooling.md#d-31-prefer-zero-deps-exact-pin) otherwise. See [R-8](rejected.md#r-8-riverpod-provider-bloc-for-state).
|
||||
- **Rationale:** SDK-shipped, zero deps, trivial to fake in tests (hand-rolled fakes in [D-25](testing.md#d-25-mocks--mocktail-at-io-hand-rolled-fakes-for-changenotifiers)). Violates [D-31 prefer-zero-deps](tooling.md#d-31-prefer-zero-deps-exact-pin) otherwise. See [R-8](../rejected/architecture.md#r-8-riverpod--provider--bloc-for-state).
|
||||
- **Cost:** No codegen ergonomics; manual `notifyListeners()` discipline. The `ListenableBuilder.listenable` contract rejects rebuilds outside the subscribed notifier — intentional.
|
||||
- **Raised by:** 2026-04-21 planning.
|
||||
|
||||
@@ -69,58 +113,14 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
- **Rationale:** (1) tmux is battle-tested — no new persistence code to review. (2) The pane subsystem stays neutral; Claude-specific behaviour lives in `builtin.claude`. (3) Keying by git root means the user doesn't manage session names manually — opening a repo is enough. (4) "Always one primary" removes a failure mode: there's never "no Claude to talk to." (5) Secondaries stay frictionless — the user spawns and closes them at will without breaking the primary.
|
||||
- **Cost:** Requires tmux on the PATH of the daemon's runtime environment (reasonable for Linux + macOS; Windows support via WSL or a separate approach). Killing a primary (via the daemon on shutdown) still leaves the detached tmux session around until the next clide start re-attaches; acceptable but worth documenting for support. Secondary numbering (`-1`, `-2`, …) resets between clide runs since ephemeral state is lost — also acceptable.
|
||||
- **Raised by:** 2026-04-22 planning, Tier 1 implementation.
|
||||
- **Cross-reference:** [`D-5`](#d-5-dart-core-sidecar-dissolved-ptyc-as-pql-peer) (ptyc as the spawn primitive tmux runs under), [`D-6`](#d-6-cli-and-event-surface-contract) (pane.\* IPC surface), [`R-9`](rejected.md#r-9-port-planning-tooling-into-clide) (why per-repo scoping via git root matches the wrap-don't-duplicate theme).
|
||||
|
||||
### D-1: CLI-first, not MCP
|
||||
- **Date:** 2026-04-20 (was ADR 0001; ported from the claudian lineage)
|
||||
- **Amendment (2026-05-15):** D-1's intent — the CLI is the *primary* agent-facing surface, with the same contract as pql — stands. An additional `/ide`-compatible MCP surface is added per [D-68](#d-68-dual-integration-surface-bash-cli-primary-mcp-secondary); both wrap the same in-process dispatcher. The escape-hatch line in this record's Cost ("nothing here precludes adding [MCP] later that shells out to the same CLI") is realised — the MCP server does not bypass the CLI's surface, it offers a second transport to it.
|
||||
- **Decision:** Claude talks to clide exclusively via Bash (`clide …`). No MCP server. No protocol layer in Claude's face. The CLI uses the same exit-code + stderr-JSON contract as pql.
|
||||
- **Context:** The two mainstream options for the agent-facing surface were an MCP server or a plain Bash CLI matching pql's contract.
|
||||
- **Rationale:** Same mental model as pql for the agent — one tool-use pattern covers both. No MCP runtime to host, authenticate, or keep in sync with client versions. User/Claude parity is easier to enforce: every CLI subcommand must have a UI affordance in the Flutter app and vice versa ([D-6](#d-6-cli-and-event-surface-contract)). Claude Code's `Bash(clide *)` allow rule is the only configuration clide needs on the agent side.
|
||||
- **Cost:** If an MCP-only integration becomes compelling later (e.g. a multi-agent scenario), nothing here precludes adding one that shells out to the same CLI.
|
||||
- **Raised by:** Ported from the claudian lineage.
|
||||
|
||||
### D-3: pql as supporter tool; clide wraps, never duplicates
|
||||
- **Date:** 2026-04-20 (was ADR 0003; ported from the claudian lineage)
|
||||
- **Decision:** Two complementary rules. **(1) Wrap, don't duplicate.** Clide never re-implements backlinks, ranking, frontmatter parsing, or wikilink resolution for query purposes. If a capability is missing in pql, it is added upstream in pql's repo and clide bumps the dependency. The only place clide contains pql logic is `lib/src/pql/` — pure shell-outs to the `pql` binary. **(2) pql is a clide subsystem when clide is present in the repo.** On load, clide writes its current state into `.pql/config.yaml` — no conditional sync. Clide only stomps keys it manages (starting with `ignore_files:` — see [D-4](#d-4-ignore-file-strategy)). Other pql config keys are left alone. Clide does **not** touch pql's index/cache data under `<repo>/.pql/` — that stays pql's private store.
|
||||
- **Context:** [`pql`](https://github.com/postmeridiem/pql) is a pre-existing Go CLI that indexes a markdown-bearing directory tree into SQLite and exposes frontmatter, wikilinks, tags, headings, and bases through a query surface. Clide needs those capabilities for its Query panel, canvas drivers, graph view, and any feature that needs to know structure.
|
||||
- **Rationale:** One source of truth for markdown semantics. Any new query capability the UI wants goes through a pql upstream PR, not a local workaround. Users never have to learn pql's config file to get consistent behaviour — clide manages it. The arrow clide → pql is never inverted: pql stays ignorant of its wrapper.
|
||||
- **Cost:** Clide's `lib/src/pql/` package is deliberately thin. pql is also the **only** query engine — Obsidian-style inline "bases" are explicitly not supported; queries live at the repo level. In repos without clide, pql works standalone unaffected.
|
||||
- **Raised by:** Ported from the claudian lineage. Load-bearing for [D-39](process.md#d-39-planning-tooling-lives-in-pql).
|
||||
|
||||
### D-4: Ignore file strategy
|
||||
- **Date:** 2026-04-20 (was ADR 0004; ported from the claudian lineage)
|
||||
- **Decision:** One mechanism everywhere: the `ignore_files:` list in `.pql/config.yaml`. Ordered list of gitignore-shaped files; later entries win on per-pattern conflicts. pql defaults to `ignore_files: [.gitignore]`. Per [D-3](#d-3-pql-as-supporter-tool-clide-wraps-never-duplicates), clide writes the list on load — `[.gitignore, .clideignore]` if `.clideignore` exists, else `[.gitignore]`. `.clideignore` carries **only** the clide-specific deviations from `.gitignore` (supports `!pattern` negations); never duplicate gitignore's contents. Walker magic: none except `.git/` — every other tool-owned dir (`.pql/`, `.clide/`) is added to `.gitignore` at install time; exclusion flows through the normal `ignore_files:` chain.
|
||||
- **Context:** Every file-enumerating surface in clide (pql query panels, canvas drivers, graph view, file watchers, pane lists, file tree) needs to skip the obvious junk — `vendor/`, `node_modules/`, `dist/`, build artifacts — or results drown in noise. Clide's working assumption is that the git repo *is* the workspace — no separate "vault" concept.
|
||||
- **Rationale:** Users get one config knob, in a file they might already know (pql users) or never need to touch (clide-only users). `.clideignore` is short by design — it's deltas, not a full list. Sidecar consumers read the same key and apply identical precedence, so Claude and the user always see the same filtered surface.
|
||||
- **Cost:** Removing clide from a repo leaves pql working with vanilla defaults (clide's last-written `ignore_files:` stays until pql or the user rewrites it; worth reconsidering during uninstall design).
|
||||
- **Raised by:** Ported from the claudian lineage.
|
||||
|
||||
### D-5: Dart core; sidecar dissolved; `ptyc` as pql-peer
|
||||
- **Date:** 2026-04-20 (was ADR 0005; supersedes [R-2](rejected.md#r-2-go-sidecar))
|
||||
- **Amendment (2026-04-23):** The separate daemon process and two-package layout are dissolved per [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server). Dart-core principle survives; the daemon binary does not.
|
||||
- **Amendment (2026-05-07):** `ptyc` retired. PTY spawning moved to Dart FFI `forkpty()` (`lib/src/pty/native_pty.dart`). The `ptyc/` source tree, `PtySession`, and `scm_rights.dart` are removed. `pql` remains the sole external supporter tool.
|
||||
- **Amendment (2026-05-17):** `forkpty()` replaced with `posix_openpt()` + `posix_spawn()` (T-96). `forkpty` calls `fork()` underneath, which is unsafe in the multithreaded Dart VM: ~5% of spawns deadlocked in the child before `execve` due to libc locks held by ghost-threads at fork time. `posix_spawn` uses `vfork` under glibc/musl/macOS, keeping the parent suspended until `execve` completes — no Dart code runs in the child. Side benefit: dropped the `libutil.so.1` dynamic dependency; PTY now resolves entirely against libc via `DynamicLibrary.process()`.
|
||||
- **Decision:** Three moves. **(1) Dart is the core language.** Everything that used to live under `sidecar/` — IPC server, CLI dispatch, process management, file watching, git shell-outs, pql wrapper — is written in Dart. Two execution modes of one Dart AOT binary: `clide <subcommand>` (one-shot, pql-style) and `clide --daemon` (long-running, owns PTYs and subprocesses, survives app restarts). The Flutter app imports the Dart core as a library *and* connects to the daemon over IPC. **(2) The sidecar directory dissolves.** Layout is `app/` (Flutter UI), `lib/` (Dart core), `bin/clide.dart` (AOT entry), `ptyc/` (C helper), no `sidecar/`, no Go module. **(3) `ptyc` is a pql-peer supporter tool.** Small C binary that does `posix_openpt` + `fork` + `exec` + fd-passing via `SCM_RIGHTS`; clide wraps it the same way it wraps pql. Shells out for every PTY (terminal pane, tmux session, Claude, LSP server, debug adapter — one code path). Consumers other than clide can use `ptyc` standalone.
|
||||
- **Context:** [R-2](rejected.md#r-2-go-sidecar) picked Go for the sidecar/CLI on two premises: (a) the heavy work belongs in a language separate from the UI layer, and (b) pql is Go so the muscle memory transfers. On reassessment, both premises broke: the "heavy work" is I/O-bound glue that `dart:io` covers cleanly — the real choice was **separate process vs shared language**, and separate-process is what matters. PTY is the one place Dart is genuinely weak (multi-threaded VM can't safely `fork()`), and once you accept a small native helper, *nothing else* needs to be in the same language.
|
||||
- **Rationale:** One toolchain for the IDE proper (Flutter + Dart). C toolchain needed only to build `ptyc` — tiny, rarely-changing. Session persistence stays because PTY master fds live in the Dart daemon process, not the app. `ptyc` naming: **p** for *project* (parallel to pql's *project query language*), **ptyc** reads as both "PTY + child" (domain vocabulary) and "PTY + C" (implementation language). Usable from Dart, Python, Go, shell — anywhere a subprocess can be spawned and a fd received.
|
||||
- **Cost:** Rust remains an escape hatch, not a plan. If a Dart limit later forces a second native helper (file-watching at scale on macOS, a tree-sitter host, etc.), the precedent is: new native need → new supporter tool, peer of pql and `ptyc`. Never a second "core language." Supply-chain gates stay, shape changes — Go `govulncheck` removed, Dart advisories review + exact-pin stays, `ptyc` gets a "read the 150 lines" review checklist (see `make security`).
|
||||
- **Raised by:** 2026-04-20 reassessment. See also the `ptyc` naming note in the original ADR (read as Project Terminal Controller / PTY+C / PTY+child).
|
||||
|
||||
### D-6: CLI and event surface contract
|
||||
- **Date:** 2026-04-20 (was ADR 0006)
|
||||
- **Decision:** The CLI is organised into **subsystems**. Each subsystem owns a noun, a set of verbs, and a set of events. The set is closed at any point in time (documented); growth is additive (new verbs, new events — never renaming existing ones without a version bump). Initial subsystems (by tier): `pane`, `tab`, `open`, `editor`, `panel`, `tree`, `git`, `pql`, `canvas`, `graph`, `theme`, `settings`, `project`. Two umbrella entry points sit outside any subsystem: `clide tail --events [--filter <subsystem>[:<id>]]` and `clide status`. Command shape: `clide <subsystem> <verb> [<positional>...] [--flag ...] [-- argv...]`. Exit codes parity with pql (`0/1/2/3/4` + `64-78` sysexits reserved); diagnostic JSON on **stderr** on non-zero exit; stdout stays machine-parseable on success. Events are JSON objects, one per line, with `v`, `ts`, `type` (`<subsystem>.<verb_past|noun_changed>`), `subsystem`, `id`, and `payload`; binary payloads base64. Every state-changing command emits at least one event; read-only commands emit nothing. Replay buffer per subsystem (default depth 16) so late subscribers still see recent effects. Parity rule: every UI affordance has a matching CLI verb (or a follow-up task naming the verb); every CLI verb surfaces in the UI (or documents why it's Claude-only).
|
||||
- **Context:** [D-1](#d-1-cli-first-not-mcp) established that Claude drives clide via a Bash CLI. That decided the *channel* — it did not define the *surface*. CLAUDE.md stated the rule colloquially ("every CLI subcommand has a UI affordance … if you add one side without the other, the feature is incomplete"); this record restates it as an implementable contract that satisfies user/Claude parity, daemon-as-authoritative-state, and pql-style ergonomics at once.
|
||||
- **Rationale:** Surface is enumerable — adding a subsystem means adding a row and specifying verbs + events. Wire schema is versioned (`v: 1` starting point; compatibility breaks bump the major and land alongside a `pubspec.yaml` `schema_version:` bump — see [Q-5](questions-architecture.md#q-5-ipc-wire-format-stability)). Events are the only UI→app state channel; the Flutter app does not poll. Extensions inherit this — a Dart extension publishes a subsystem; the same registration pipeline exposes it to Claude via the CLI.
|
||||
- **Cost:** Replay-buffer memory per subsystem (cheap — most emit seldom). Back-pressure on firehose streams ([Q-2](questions-architecture.md#q-2-back-pressure-on-event-streams)), authorisation granularity ([Q-1](questions-architecture.md#q-1-authorisation-granularity)), and event persistence ([Q-3](questions-architecture.md#q-3-event-persistence-audit-undo)) are all deferred until Tier 1 is in real use.
|
||||
- **Raised by:** 2026-04-20 planning.
|
||||
- **Cross-reference:** [`D-5`](#d-5-dart-core-sidecar-dissolved-ptyc-as-pql-peer) (ptyc as the spawn primitive tmux runs under), [`D-6`](#d-6-cli-and-event-surface-contract) (pane.\* IPC surface), [`R-9`](../rejected/process.md#r-9-port-planning-tooling-into-clide) (why per-repo scoping via git root matches the wrap-don't-duplicate theme).
|
||||
|
||||
### D-43: Design handoff — adopt token palettes, reject Material wrapper
|
||||
- **Date:** 2026-04-22
|
||||
- **Decision:** The claude.ai/design handoff (`docs/claude-design/`) delivers hi-fi mockups, interaction flows, a design system, and four theme palettes (clide, midnight, paper, terminal) as Dart files using `MaterialApp`/`ThemeData`. We adopt the colour tokens, layout annotations, typography direction, and syntax highlighting palettes. We reject the `MaterialApp` wrapper — tokens are translated into our existing YAML theme pipeline and `SurfaceTokens` (per [D-7](#d-7-app-root-is-bare-widgetsapp)). The design files stay in `docs/claude-design/` as reference; they are not runtime assets.
|
||||
- **Rationale:** The design's value is in the palette + layout + component vocabulary, not in the delivery format. Material's `ThemeData` fights our bare-`WidgetsApp` + `CustomPaint` stance. Translating tokens preserves design intent without absorbing Material's widget opinions.
|
||||
- **Cost:** Manual translation of four theme files into YAML. Ongoing: any design refresh needs the same translation pass.
|
||||
- **Cross-reference:** [D-7](#d-7-app-root-is-bare-widgetsapp), [D-9](#d-9-three-tier-theme-pipeline), [R-12](rejected.md#r-12-materialapp-wrapper-from-design-handoff).
|
||||
- **Cross-reference:** [D-7](#d-7-app-root-is-bare-widgetsapp), [D-9](#d-9-three-tier-theme-pipeline), [R-12](../rejected/architecture.md#r-12-materialapp-wrapper-from-design-handoff).
|
||||
- **Raised by:** 2026-04-22 design handoff review.
|
||||
|
||||
### D-44: Four bundled themes — clide, midnight, paper, terminal
|
||||
@@ -128,18 +128,19 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
- **Decision:** Ship four bundled themes replacing the single summer-night preset. `clide` (cool near-black + periwinkle, default), `midnight` (VS Code-adjacent muted dark), `paper` (drafting-sheet light), `terminal` (near-black + amber). All share the same semantic token names. Source palettes in `docs/claude-design/themes/`; runtime YAML under `lib/kernel/src/theme/themes/`.
|
||||
- **Rationale:** Summer-night was a placeholder carried from the legacy TUI. The design system delivers a coherent set of four that covers dark, muted-dark, light, and monochrome workflows.
|
||||
- **Cost:** Summer-night users lose their theme (acceptable — it was dev-only). Four YAML files to maintain.
|
||||
- **Cross-reference:** [D-43](#d-43-design-handoff-adopt-token-palettes-reject-material-wrapper), [D-22](accessibility.md#d-22-wcag-aa-contrast-gate-on-bundled-themes).
|
||||
- **Cross-reference:** [D-43](#d-43-design-handoff--adopt-token-palettes-reject-material-wrapper), [D-22](accessibility.md#d-22-wcag-aa-contrast-gate-on-bundled-themes).
|
||||
- **Raised by:** 2026-04-22 design handoff review.
|
||||
|
||||
### D-45: Syntax highlighting tokens in the theme pipeline
|
||||
- **Date:** 2026-04-22
|
||||
- **Decision:** Add syntax-role colour tokens to `SurfaceTokens`: keyword, type, string, number, comment, method, punctuation. Each bundled theme defines these. The editor and diff views consume them; tree-sitter (when it lands per [Q-15](questions-architecture.md#q-15-editor-tab-full-lsp-vs-tree-sitter-only)) maps grammar scopes to these tokens.
|
||||
- **Decision:** Add syntax-role colour tokens to `SurfaceTokens`: keyword, type, string, number, comment, method, punctuation. Each bundled theme defines these. The editor and diff views consume them; tree-sitter (when it lands per [Q-15](../questions/process.md#q-15-editor-tab--full-lsp-vs-tree-sitter-only-highlight)) maps grammar scopes to these tokens.
|
||||
- **Rationale:** The design system ships syntax palettes per theme. Adding them now means the token surface is ready when syntax highlighting lands.
|
||||
- **Cost:** Seven new fields on `SurfaceTokens`. Default resolution falls back to semantic roles (keyword → accent, comment → textMuted, etc.) so themes that don't declare syntax tokens still compile.
|
||||
- **Raised by:** 2026-04-22 design handoff review.
|
||||
|
||||
### D-47: Interaction model — Claude-is-home layout
|
||||
- **Date:** 2026-04-22
|
||||
- **Amendment (2026-06-06):** Rule (1) (prompt-bar Y invariant across all states) gains one documented exception: the bottom **output dock** ([D-87](#d-87-outputlog-dock--bottom-toggled-read-only-logs--problems)) may re-baseline the prompt bar by pushing Claude up when it opens, capped so Claude stays the largest surface (≥ 50% of the middle column). Closed dock → original baseline. The dock is the *only* surface permitted to move the prompt-bar Y; everything else still obeys rule (1).
|
||||
- **Decision:** The prompt bar is pinned to a fixed Y-position in the middle column; every other surface makes room *around* Claude — never on top, never pushing the prompt off-Y. Three hard rules: (1) prompt bar Y-position is invariant across all states (open, collapsed, focus, editor, viewer); (2) the three bottom strips (left icon rail, app strip, right icon rail) align to one continuous horizontal line; (3) Claude is always the largest surface when present. The three-column layout from [D-11](#d-11-panel-manager-is-kernel-layout-is-data-three-column-is-a-preset) is refined: left = overview (tickets, decisions, files, git, PRs), middle = Claude (+ optional editor above), right = context (viewer, pql graph, links, images). Both side panels have a bottom icon rail for section switching; keyboard: `⌥1–5` (left), context-type switcher (right).
|
||||
- **Rationale:** "Claude is home" means the prompt never moves, regardless of what opens or closes around it. Every layout mutation respects this invariant. The three-column refinement assigns purpose to columns rather than leaving them generic.
|
||||
- **Cost:** The prompt bar invariant constrains future layout presets — any preset that repositions Claude must explicitly break this rule. Editor mode (see [D-49](#d-49-editor-mode-inline-above-claude-viewer-swap)) is the only case where another surface shares the middle column, and it opens *above* Claude rather than displacing it.
|
||||
@@ -148,7 +149,7 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
|
||||
### D-48: Chrome budget — no tabs, no breadcrumbs, keyboard-first
|
||||
- **Date:** 2026-04-22
|
||||
- **Decision:** Clide deletes classic IDE chrome: no buffer tabs, no breadcrumbs, no VS Code-style activity bar, no separate status bar row (merged into app strip). Total persistent chrome: 2 edge arrows (collapse toggles), 1 hover-only `⛶` glyph per panel (focus mode), 0 always-visible buttons beyond icon rails. `⌘P` overlay is the fuzzy finder — no layout shift. Keyboard is the primary interaction surface; icons are escape hatches. Files open individually; opening a second file closes the first (split on explicit command — deferred, see [Q-27](questions-architecture.md#q-27-two-editor-split)).
|
||||
- **Decision:** Clide deletes classic IDE chrome: no buffer tabs, no breadcrumbs, no VS Code-style activity bar, no separate status bar row (merged into app strip). Total persistent chrome: 2 edge arrows (collapse toggles), 1 hover-only `⛶` glyph per panel (focus mode), 0 always-visible buttons beyond icon rails. `⌘P` overlay is the fuzzy finder — no layout shift. Keyboard is the primary interaction surface; icons are escape hatches. Files open individually; opening a second file closes the first (split on explicit command — deferred, see [Q-27](../questions/architecture.md#q-27-two-editor-split)).
|
||||
- **Rationale:** Every pixel of chrome that isn't Claude is a tax on the "Claude is home" principle. Tabs and breadcrumbs are navigation affordances for a multi-buffer editor; clide's editor is a secondary surface (viewer ↔ editor swap per [D-49](#d-49-editor-mode-inline-above-claude-viewer-swap)), not a primary one. The fuzzy finder (`⌘P`) replaces all navigation chrome.
|
||||
- **Cost:** Users accustomed to VS Code/IntelliJ tab workflows have no tabs to fall back on. Mitigated by `⌘P` fuzzy find being the universal navigation path. Resolves T-22 (multi-buffer editor tabs) as rejected in favour of this approach.
|
||||
- **Cross-reference:** [D-47](#d-47-interaction-model-claude-is-home-layout), [D-49](#d-49-editor-mode-inline-above-claude-viewer-swap).
|
||||
@@ -158,7 +159,7 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
- **Date:** 2026-04-22
|
||||
- **Decision:** Editor invoked via `⌘E` on a file or `✎` icon in a viewer. Editor lifts *above* Claude in the middle column, occupying 30–40% of vertical space; Claude keeps the remainder; prompt bar Y unchanged. Close with `⌘W`. Draggable divider between editor and Claude. The viewer (`👁`) and editor (`✎`) are mutually exclusive for the same file — a `✎` click on a viewer promotes the file to editor in the middle column and snaps the right panel back to nav; a `👁` click on an editor demotes the file to viewer in the right panel and closes the editor. Different files can coexist (editor on `main.dart` + viewer on `README.md`). When editor is open on `.md`, the viewer auto-opens with live sync to editor content; no auto-viewer for non-renderable files (`.dart`, `.yaml`, etc.).
|
||||
- **Rationale:** The editor is not a primary surface — it's a temporary intervention. Claude's prompt bar must never move ([D-47](#d-47-interaction-model-claude-is-home-layout)), so the editor opens above, not replacing. The viewer ↔ editor swap prevents two surfaces showing the same file simultaneously, which simplifies state management and avoids confusion about which surface is authoritative.
|
||||
- **Cost:** Only one file in the editor at a time (no tabs per [D-48](#d-48-chrome-budget-no-tabs-no-breadcrumbs-keyboard-first)). Power users wanting two files side-by-side must wait for split (see [Q-27](questions-architecture.md#q-27-two-editor-split)).
|
||||
- **Cost:** Only one file in the editor at a time (no tabs per [D-48](#d-48-chrome-budget-no-tabs-no-breadcrumbs-keyboard-first)). Power users wanting two files side-by-side must wait for split (see [Q-27](../questions/architecture.md#q-27-two-editor-split)).
|
||||
- **Cross-reference:** [D-47](#d-47-interaction-model-claude-is-home-layout), [D-48](#d-48-chrome-budget-no-tabs-no-breadcrumbs-keyboard-first).
|
||||
- **Raised by:** 2026-04-22 interaction model spec (Wireframe — Flows v3).
|
||||
|
||||
@@ -167,7 +168,8 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
- **Decision:** The right panel responds to Claude's content references automatically: (1) right open + empty → panel holds footprint, stays empty; (2) right open + viewer loaded + Claude links `foo.md` → swap in, replaces current viewer; (3) right collapsed + Claude links `foo.md` → badge on spine ("2"), no layout shift; (4) editor open on `.md` → viewer auto-opens with live sync; (5) editor on non-renderable file → no auto-viewer.
|
||||
- **Rationale:** Claude is the driver; the context panel is reactive. Auto-swapping when the panel is open reduces user clicks. Badging when collapsed respects the user's decision to collapse — no involuntary layout shifts.
|
||||
- **Cost:** The auto-swap requires the daemon (or Claude integration) to emit structured content references, not just terminal text. This implies a lightweight parser or event that identifies file references in Claude's output — deferred to implementation.
|
||||
- **Cross-reference:** [D-47](#d-47-interaction-model-claude-is-home-layout), [D-51](#d-51-panel-collapse-12px-spine-with-badge).
|
||||
- **Amendment (2026-06-06):** Clauses (2) and (3) — auto-swap from parsed Claude output, and the spine badge while collapsed — are **superseded** by the "give Claude hands" push ([T-208](../../)). Rather than clide scraping the terminal for file references (the "lightweight parser" the Cost note deferred), the agent now drives the reader **explicitly** via the `clide ui open markdown <path>` verb (T-231) and `ui.open` → diff (T-233), on contract with [D-1](#d-1-cli-first-not-mcp) / [D-6](#d-6-cli-and-event-surface-contract). The open is therefore an intentional agent act, not a passive notification, so the collapsed-spine badge (clause 3) is dropped. Clause (1) holds and clause (5) holds by default. The one behavior give-clide-hands did **not** deliver is clause (4) — the live-sync read-mirror of an open editor buffer — which remains UI-owned and is tracked by T-36 (re-scoped to that single clause and re-homed under T-259, the interaction-model epic).
|
||||
- **Cross-reference:** [D-47](#d-47-interaction-model-claude-is-home-layout), [D-51](#d-51-panel-collapse-12px-spine-with-badge), [D-1](#d-1-cli-first-not-mcp), [D-6](#d-6-cli-and-event-surface-contract).
|
||||
- **Raised by:** 2026-04-22 interaction model spec (Wireframe — Flows v3).
|
||||
|
||||
### D-51: Panel collapse — 12px spine with badge
|
||||
@@ -196,7 +198,7 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
|
||||
### D-54: Keyboard map — canonical shortcuts
|
||||
- **Date:** 2026-04-22
|
||||
- **Decision:** Canonical keyboard shortcuts (cross-platform, `⌘` = `Ctrl` on Linux): `⌘P` fuzzy find overlay; `⌘⇧1` / `⌘⇧3` collapse/expand left / right panel; `⌘1` / `⌘2` / `⌘3` focus left / middle / right panel; `⌘.` toggle focus mode on focused panel; `⌥1–⌥5` left-panel section switch (tickets, decisions, files, git, pr); `⌘E` open current file in editor; `⌘W` close editor / dismiss viewer; `Esc` exit focus mode / close fuzzy finder / dismiss viewer. Responsive breakpoints: ≥ 1600px splits relax toward 30%; 1200–1600px default (L 200px, R 220px, middle flex); < 1200px splits toward 40%, consider auto-collapse right; < 1000px deferred (see [Q-26](questions-architecture.md#q-26-small-screen-layout)).
|
||||
- **Decision:** Canonical keyboard shortcuts (cross-platform, `⌘` = `Ctrl` on Linux): `⌘P` fuzzy find overlay; `⌘⇧1` / `⌘⇧3` collapse/expand left / right panel; `⌘1` / `⌘2` / `⌘3` focus left / middle / right panel; `⌘.` toggle focus mode on focused panel; `⌥1–⌥5` left-panel section switch (tickets, decisions, files, git, pr); `⌘E` open current file in editor; `⌘W` close editor / dismiss viewer; `Esc` exit focus mode / close fuzzy finder / dismiss viewer. Responsive breakpoints: ≥ 1600px splits relax toward 30%; 1200–1600px default (L 200px, R 220px, middle flex); < 1200px splits toward 40%, consider auto-collapse right; < 1000px deferred (see [Q-26](../questions/architecture.md#q-26-small-screen-layout--1000px)).
|
||||
- **Rationale:** These shortcuts follow the "keyboard is the primary surface" principle from [D-48](#d-48-chrome-budget-no-tabs-no-breadcrumbs-keyboard-first). The set is minimal and covers all layout operations. `⌘.` for focus mode follows VS Code precedent (quick-fix → general "do the thing").
|
||||
- **Cost:** Some shortcuts may conflict with OS-level bindings on specific Linux desktops; the keybinding resolver ([D-17](extensions.md#d-17-panels-are-extension-shaped-from-day-one)) allows user override.
|
||||
- **Cross-reference:** [D-47](#d-47-interaction-model-claude-is-home-layout), [D-48](#d-48-chrome-budget-no-tabs-no-breadcrumbs-keyboard-first), [D-52](#d-52-focus-mode-full-window-takeover).
|
||||
@@ -226,13 +228,14 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
- `/decisions/`, `/docs/`, `/legacy/` — unchanged
|
||||
- **Rationale:** One package means one `pubspec.yaml`, one `flutter analyze`, one `flutter test`, no `cd` gymnastics, no cross-package import barriers. The IPC server running in-process eliminates the daemon lifecycle (start, stop, reconnect, pid file). If the app crashes, tmux sessions survive; the app re-attaches on restart. The CLI client in C is ~100 lines (socket connect + JSON exchange) with the same contract as pql.
|
||||
- **Cost:** If the Flutter app is not running, Claude's `clide` commands fail. In practice this is acceptable — the IDE being closed means the user isn't working. A future "headless mode" could start the Flutter engine without a window if needed.
|
||||
- **Cross-reference:** [D-5](#d-5-dart-core-sidecar-dissolved-ptyc-as-pql-peer) (amended), [D-41](#d-41-claude-panes-one-primary-per-repo-tmux-backed) (tmux persistence), [D-1](#d-1-cli-first-not-mcp) (CLI-first surface preserved via C client).
|
||||
- **Amendment (2026-05-19):** Implemented in T-99 across eight slices (T-124 server, T-125 argv translator, T-126 C client, T-127 socket loopback replacing InProcessClient, T-128 legacy-IPC cleanup, T-129 event streaming, T-130 MCP companion, T-131 this wrap-up). Per-workspace unix socket at the D-70 path; D-71 chmod gate; D-72 multi-connection serial dispatch; D-73 MCP transport. The C `clide` client lives at `native/clide-cli/clide.c` and ships with `make clide-cli`. Only the socket IPC model survives — InProcessClient + IsolateClient + Backend gone.
|
||||
- **Cross-reference:** [D-5](#d-5-dart-core-sidecar-dissolved-ptyc-as-pql-peer) (amended), [D-41](#d-41-claude-panes-one-primary-per-repo-tmux-backed) (tmux persistence), [D-1](#d-1-cli-first-not-mcp) (CLI-first surface preserved via C client), [D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic) / [D-71](#d-71-ipc-socket-access-gated-by-chmod-0600-on-socket--parent) / [D-72](#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate) (implementation contracts).
|
||||
- **Raised by:** 2026-04-23 architectural simplification.
|
||||
|
||||
### D-57: Frameless custom chrome with per-column 24px hats
|
||||
- **Date:** 2026-04-23
|
||||
- **Decision:** The OS-native title bar is hidden. Each of the three columns wears its own 24px "hat" that serves as both a drag region and a host for window controls. Left hat: macOS traffic lights (Linux/Windows: plain drag). Center hat: `clide > branch` label, always present. Right hat: minimize/maximize/close glyph buttons on Linux/Windows (macOS: plain drag). Entire hat surface is draggable; buttons opt out of hit testing. When a column collapses to a 12px spine, its hat shrinks to a 12px drag cap — no buttons, still draggable. The center hat never collapses. Three `ChromeStyle` variants: `seam` (default desktop — full hats), `prompt` (center hat only — presentations/focus), `inline` (web/wasm — no hats, browser owns window controls). Persisted in settings as `app.chromeStyle`. Platform bridge via `MethodChannel('clide/window')` — custom GTK C and Cocoa Swift handlers, no third-party package.
|
||||
- **Resolves:** [Q-6](questions-architecture.md#q-6-window-chrome-native-frame-vs-frameless-custom).
|
||||
- **Resolves:** [Q-6](../questions/architecture.md#q-6-window-chrome--native-frame-vs-frameless-custom).
|
||||
- **Rationale:** The GTK headerbar wastes 30+ vertical pixels and clashes with the custom theme. Per-column hats add zero net rows — they reuse the space each column header already occupied. Custom FFI avoids a `window_manager` dependency (D-31). The `ChromeStyle` enum keeps web builds clean and allows user override.
|
||||
- **Cost:** ~150 lines C (GTK) + ~100 lines Swift (Cocoa) for the platform channel. Window controls become unreachable when their column collapses — mitigated by keyboard shortcuts (`⌘Q` to close, `⌘1`/`⌘3` to expand).
|
||||
- **Cross-reference:** [D-47](#d-47-interaction-model-claude-is-home-layout) (center hat always visible), [D-51](#d-51-panel-collapse-12px-spine-with-badge) (spine-cap behavior).
|
||||
@@ -248,11 +251,222 @@ Core, rendering, IPC, kernel, panel manager.
|
||||
|
||||
### D-68: Dual integration surface — Bash CLI primary, MCP secondary
|
||||
- **Date:** 2026-05-15
|
||||
- **Decision:** clide exposes two integration surfaces over the same in-process `DaemonDispatcher`. **(1) Bash CLI over Unix socket — primary.** Per [D-1](#d-1-cli-first-not-mcp) and [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server), a thin C client (`clide …`) connects to a per-user Unix socket served in-process by the Flutter app, exchanges JSON-lines, and exits. This is the surface Claude-Code-in-a-pane uses; it is also the surface for human shell use, scripts, and external editor integrations. Full action surface — `pane.*`, `files.*`, `editor.*`, `git.*`, `pql.*`, …. **(2) `/ide`-compatible MCP server — secondary.** clide additionally serves an MCP endpoint compatible with Claude Code's `/ide` integration (the same protocol VS Code and JetBrains plugins serve). Minimum tools: `mcp__ide__getDiagnostics`, `mcp__ide__executeCode`. Optional `mcp__clide__*` namespace exposing high-leverage clide tools is deferred to [Q-32](../questions/architecture.md#q-32-mcp-tool-surface-minimum-slash-ide-or-extended-clide-tools). Transport choice deferred to [Q-33](../questions/architecture.md#q-33-mcp-transport-sse-websocket-stdio-or-all). The MCP server wraps the *same* `DaemonDispatcher`; there is no second source of truth.
|
||||
- **Amendment (2026-05-19):** Both surfaces implemented in T-99. CLI lands as `IpcServer` over the unix socket (T-124, per D-70/71/72) with the C client at `native/clide-cli/clide.c` (T-126); the argv grammar lives in `lib/src/cli/argv_to_request.dart` (T-125). MCP lands as `McpServer` over HTTP+SSE (T-130, per D-73) — the transport choice closed Q-33. The two `/ide` minimum tools ship as stubs; real implementations follow as Q-32 resolves the broader tool-surface question.
|
||||
- **Decision:** clide exposes two integration surfaces over the same in-process `DaemonDispatcher`. **(1) Bash CLI over Unix socket — primary.** Per [D-1](#d-1-cli-first-not-mcp) and [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server), a thin C client (`clide …`) connects to a per-user Unix socket served in-process by the Flutter app, exchanges JSON-lines, and exits. This is the surface Claude-Code-in-a-pane uses; it is also the surface for human shell use, scripts, and external editor integrations. Full action surface — `pane.*`, `files.*`, `editor.*`, `git.*`, `pql.*`, …. **(2) `/ide`-compatible MCP server — secondary.** clide additionally serves an MCP endpoint compatible with Claude Code's `/ide` integration (the same protocol VS Code and JetBrains plugins serve). Minimum tools: `mcp__ide__getDiagnostics`, `mcp__ide__executeCode`. Optional `mcp__clide__*` namespace exposing high-leverage clide tools is deferred to [Q-32](../questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools). Transport closed by [D-73](#d-73-mcp-transport-for-ide-is-sse-over-http) — SSE over HTTP. The MCP server wraps the *same* `DaemonDispatcher`; there is no second source of truth.
|
||||
- **Context:** [D-1](#d-1-cli-first-not-mcp) chose CLI-first over MCP-only because MCP alone doesn't cover the action surface clide needs — Claude Code's `/ide` MCP exposes only two narrow tools (`getDiagnostics`, `executeCode`), enough for Claude to read diagnostics and run Jupyter cells but not enough to *drive* an IDE. The CLI surface gives full reach. But for users who run Claude Code *outside* clide and connect via `/ide`, MCP is the only path Claude Code knows; not serving it means clide is invisible to that workflow. The two surfaces are complementary, not alternatives. Reinforced by the [2026-05-14 consultant review](../../consultants.md): the architect flagged the absent socket server as the most critical drift; user confirmed the socket server (D-56 path a) plus an MCP companion.
|
||||
- **Rationale:** Both surfaces wrap the same dispatcher, so neither becomes a second source of truth. CLI remains the contract user/Claude parity ([D-6](#d-6-cli-and-event-surface-contract)) is enforced against. MCP is added because the `/ide` ecosystem is real and growing — VS Code, JetBrains, Cursor, Windsurf all serve compatible MCP — and clide should be a peer there. The implementation cost is a protocol adapter + tool definitions, not duplicate business logic.
|
||||
- **Cost:** Two transports to maintain. Mitigated by both wrapping the same dispatcher: the MCP adapter is the only thing that has to track `/ide` protocol evolution. If `mcp__clide__*` tools are added (pending Q-32), surface bloat is the obvious risk — every CLI verb invites an MCP twin; resist by default, justify on user need.
|
||||
- **Cross-reference:** [D-1](#d-1-cli-first-not-mcp) (amended — see amendment line there), [D-6](#d-6-cli-and-event-surface-contract), [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server), [Q-32](../questions/architecture.md#q-32-mcp-tool-surface-minimum-slash-ide-or-extended-clide-tools), [Q-33](../questions/architecture.md#q-33-mcp-transport-sse-websocket-stdio-or-all).
|
||||
- **Cross-reference:** [D-1](#d-1-cli-first-not-mcp) (amended — see amendment line there), [D-6](#d-6-cli-and-event-surface-contract), [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server), [Q-32](../questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools), [Q-33](../questions/architecture.md#q-33-mcp-transport--sse-websocket-stdio-or-all).
|
||||
- **Raised by:** 2026-05-15 — consultant review (`consultants.md`) flagged the absent socket server (D-56 unimplemented) as the highest architectural drift; user chose option (a) "implement the server" and asked for MCP coverage alongside.
|
||||
|
||||
### D-70: IPC socket path is per-workspace, deterministic
|
||||
- **Date:** 2026-05-18
|
||||
- **Decision:** The Unix-domain IPC socket served by the Flutter app (per [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server) / [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary)) lives at `$XDG_RUNTIME_DIR/clide/<hash(workspace-root)>.sock` on Linux and `$HOME/Library/Caches/clide/<hash(workspace-root)>.sock` on macOS. The workspace root is the git toplevel (the same path the Flutter app resolved on boot). The hash is **FNV-1a 64-bit, lower-case hex (16 chars)** — deterministic, dependency-free (no `package:crypto`), matches the existing `session_naming.dart` `_hash` shape so users see one hashing pattern across clide's process boundaries. No env override. The C client (T-126) and any other consumer resolves its target socket by walking CWD up to the git toplevel and computing the same hash.
|
||||
- **Rationale:** "Repo-is-the-workspace" (CLAUDE.md guardrail) means clide instances are per-repo, so the socket must be too — a per-user-global socket would force one running clide per user and break the multi-repo workflow. The same hash on both sides ensures the shell client + the running app always agree without configuration. No env override because the deterministic path is the contract; the only reason to override is a test fixture, and tests can set `XDG_RUNTIME_DIR` to a tempdir directly. FNV-1a over a crypto hash: collision resistance isn't a security need (workspace paths are user-supplied; the path is `0600`-readable only by that user anyway); 64 bits is overkill for the cardinality (a user with 65k workspaces would still see negligible birthday collisions). Aligns with [D-41](#d-41-claude-panes-one-primary-per-repo-tmux-backed)'s tmux-socket-per-repo convention so users see one consistent pattern.
|
||||
- **Cost:** The 16-char hex prefix means socket paths aren't human-readable at a glance — `ls $XDG_RUNTIME_DIR/clide/` won't tell you which one is which repo. Acceptable; the C client never asks the user to type the path, and debugging can use a sibling `.path` file next to each socket if it becomes painful.
|
||||
- **Cross-reference:** [D-41](#d-41-claude-panes-one-primary-per-repo-tmux-backed), [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server), [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary), `lib/kernel/src/files.dart` (workspace root resolution).
|
||||
- **Raised by:** 2026-05-18 — T-99 design pass; locked in before T-124 starts so the server + client agree on path strategy.
|
||||
|
||||
### D-71: IPC socket access gated by chmod 0600 on socket + parent
|
||||
- **Date:** 2026-05-18
|
||||
- **Decision:** The IPC socket file and its parent directory ([D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic)) are created with permissions `0600` (owner read/write only) and `0700` respectively. No token-based auth at the IPC layer; the Unix file-permission check is the only gate. Capability-scoped auth for third-party Lua/Dart extensions is a separate concern and remains tracked by [Q-1](../questions/architecture.md#q-1-authorisation-granularity-on-the-ipc-socket) for when extensions actually need it.
|
||||
- **Rationale:** clide's threat model on a developer workstation is "another user on the same host should not be able to drive my IDE." File perms cover this exhaustively — the kernel enforces the check on every `connect(2)`, no userspace token comparison can match that. Adding a session token on top would be belt-and-suspenders without expanding the threat model. Capability tokens become useful when extensions can publish their own dispatcher routes and we want to gate which third-party code can reach which subsystem — but that's a Tier-6 concern.
|
||||
- **Cost:** Doesn't defend against same-uid attacks (a malicious process running as the user can connect). Accepted — same-uid is outside this layer's threat model; that's a sandboxing / capability concern that lives further out. Doesn't work on shared multi-user dev hosts where one socket needs to be reachable by multiple uids — clide isn't targeted at that workflow today.
|
||||
- **Cross-reference:** [D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic), [Q-1](../questions/architecture.md#q-1-authorisation-granularity-on-the-ipc-socket).
|
||||
- **Raised by:** 2026-05-18 — T-99 design pass.
|
||||
|
||||
### D-72: IPC server is multi-connection with serial dispatch on the main isolate
|
||||
- **Date:** 2026-05-18
|
||||
- **Decision:** The IPC server ([D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic)) is a multi-connection accept loop over `ServerSocket.listen`. Multiple clients (one-shot `clide <verb>` calls, a long-lived `clide tail --events` subscriber, the MCP adapter) can hold simultaneous connections. Each connection reads its own JSON-line stream asynchronously. **Dispatch through the existing `DaemonDispatcher` is serial on the main Flutter isolate** — the dispatcher walks one request at a time. Individual handlers are free to offload heavy or blocking work to short-lived worker isolates (the pattern `NativePty` and `SchedulerService` already use); the IPC layer doesn't impose that choice.
|
||||
- **Rationale:** Multi-connection at the socket layer is what every other concurrent operation in the codebase needs — `clide tail --events` (T-129) is structurally a long-lived subscription, the MCP adapter (T-130) lives on a separate connection from the CLI's one-shot requests, and parallel CLI invocations from a developer's shell shouldn't serialise on the I/O level. Serial dispatch on the main isolate is forced by the architecture, not chosen: subsystem handlers (PaneRegistry, FilesService, EditorRegistry, GitClient, PqlClient) hold mutable Dart objects + `ChangeNotifier`s the UI rebuilds from, and Dart isolates don't share heaps. A worker-isolate-per-connection design would have to ferry every request back to the main isolate via `SendPort` to actually execute — pure overhead with no parallel-dispatch gain. Per-handler isolate offload solves the only real problem (a slow handler janking the UI) without paying the isolate-safety tax across every subsystem.
|
||||
- **Cost:** A genuinely slow handler that doesn't offload to an isolate blocks the dispatch queue for all other connections until it returns. Mitigated by the per-handler offload pattern already in the codebase. Doesn't fit a future world where clide hosts headless workers (CI, batch jobs) that want true parallel dispatch — that's a different product shape and would warrant rethinking this decision.
|
||||
- **Cross-reference:** [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server), [D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic), `lib/src/pty/native_pty.dart` (per-handler isolate offload example), `lib/kernel/src/scheduler.dart` (same pattern).
|
||||
- **Raised by:** 2026-05-18 — T-99 design pass; user explicitly considered worker-isolate-per-connection and confirmed serial dispatch on main is the right shape given the shared-state architecture.
|
||||
|
||||
### D-73: MCP transport for /ide is SSE over HTTP
|
||||
- **Date:** 2026-05-19
|
||||
- **Decision:** The `/ide`-compatible MCP server clide ships per [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) uses **HTTP + Server-Sent Events** as its transport. The Flutter app binds an HTTP server on a random localhost port at startup, advertises itself via a discovery file at `$HOME/.claude/ide/<pid>.lock` (the format Claude Code's `/ide` command discovers), and serves: a `GET /sse` endpoint that opens a long-lived SSE stream for server-to-client JSON-RPC responses + events, and a `POST /messages` endpoint that accepts client-to-server JSON-RPC requests. Closes Q-33.
|
||||
- **Rationale:** clide's Flutter app is always-running and user-launched — the agent connects to it, not the other way around. That rules out stdio (which assumes the agent spawns the server as a subprocess). Between SSE and WebSocket, SSE wins on three counts: (a) Claude Code's existing `/ide` discovery already uses HTTP servers advertised via lock files, (b) SSE is trivially implementable on `dart:io`'s `HttpServer` (long-lived response + `data: <json>\n\n` per message — no upgrade dance, no framing), (c) JSON-RPC is fundamentally client-pushes-requests / server-pushes-responses-and-events, which maps cleanly to "POST in / SSE out." WebSocket buys bidirectional symmetry we don't need. Per [D-72](#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate) the MCP layer is just another transport that wraps the same `DaemonDispatcher` — no second source of truth.
|
||||
- **Cost:** SSE requires a long-lived HTTP response. Browsers cap concurrent SSE connections per origin at 6, but the consumers here are Claude Code instances (not browsers) and one-per-workspace is the expected fan-out. Adds an HTTP listener alongside the unix socket — a small surface increase, but the alternatives are worse. Each running clide grabs a random localhost port; no contention.
|
||||
- **Cross-reference:** [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary), [D-72](#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate), `lib/src/ipc/mcp_server.dart` (this transport's implementation lands in T-130).
|
||||
- **Raised by:** 2026-05-19 — T-130 design pass; user picked SSE over HTTP after weighing against WebSocket and stdio.
|
||||
|
||||
### D-74: IPC command schema is co-registered with the handler, validated at dispatch
|
||||
- **Date:** 2026-05-20
|
||||
- **Decision:** Each IPC command may declare a **typed argument schema** (per-arg charset/regex/range constraints, required/optional, kind). The schema is **registered alongside the handler**, not authored in a central file: the `DaemonDispatcher` registration API carries an optional schema per command, and the dispatcher accumulates a `cmd → schema` registry as commands register. `DaemonDispatcher.dispatch` validates `req.args` against the command's schema **before** invoking the handler (`lib/src/daemon/dispatcher.dart`), returning a `userError` (sysexit 64) on violation so no handler sees malformed input. Built-in command modules (`registerPaneCommands`, `registerGitCommands`, …) supply their schemas at registration; extension-contributed commands ([`CommandContribution`](../../lib/extension/src/contribution.dart), Tier 6 / [D-46](tooling.md)) carry their own. The pre-existing T-104 spot-checks — `validateGitRef` in `lib/src/git/operations.dart` and the count/path caps in `lib/src/daemon/git_commands.dart` — stay in place as **defense-in-depth** below the dispatcher, because the git client is also callable directly from the Flutter UI (not only through the dispatcher). MCP `tools/list` generation from this registry is **out of scope** here — deferred to the T-130 track.
|
||||
- **Rationale:** clide's command surface is open, not fixed: every `register*Commands()` module already contributes a *set* of commands, and the extension framework lets plugins contribute more at runtime. A central static schema map (`command_schema.dart` keyed by every known cmd) cannot see extension-contributed commands and would fight the plugin model — it was considered and rejected for exactly that reason. Co-registration keeps the schema next to the code that owns the command's meaning, supports any number of independent registrants (the normal case, not an edge case), and still yields a single validation lookup at dispatch time. Validating at the dispatcher (rather than per-handler) is what makes the constraint a contract instead of a convention: a new command can't forget to validate, and the `_ResizeArgs`-style hand-lifts scattered through handlers (see `panel_commands.dart`, T-119) collapse into the schema. No third-party validation library — the constraint vocabulary is hand-rolled per the prefer-zero-deps guardrail.
|
||||
- **Cost:** A schema must be authored for each command (build-fresh — the argv parser at `lib/src/cli/argv_to_request.dart` only knows syntactic shape, never per-command argument names, so there is nothing to "lift"). Keeping `validateGitRef` + caps as well as the schema is deliberate redundancy on the git path; the alternative (single gate at the dispatcher) would leave the UI→client call path unvalidated. Until every command has a schema, validation is opt-in per command — commands with no registered schema dispatch unvalidated, same as today.
|
||||
- **Cross-reference:** [D-6](#d-6-cli-and-event-surface-contract) (CLI/Claude parity the schema enforces), [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) / [D-72](#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate) (the dispatch path being gated), [D-46](tooling.md) (extension model the central-map alternative would have broken), `lib/src/daemon/dispatcher.dart` (the validation hook), `lib/src/git/operations.dart` + `lib/src/daemon/git_commands.dart` (the T-104 checks kept as defense-in-depth).
|
||||
- **Raised by:** 2026-05-20 — T-120 design pass; user rejected the central registry as misaligned with the extensions/plugins model and confirmed co-registration with multiple command-set registrants.
|
||||
|
||||
### D-75: Claude rendered natively from transcripts; terminal retained as general tool only
|
||||
- **Date:** 2026-05-22
|
||||
- **Decision:** clide renders the Claude conversation as **native Flutter widgets** driven by Claude Code's transcript JSONL (`~/.claude/projects/<munged-cwd>/<session-id>.jsonl`; teammate agents under `<session-id>/subagents/agent-<id>.jsonl`). The PTY/terminal emulator is **not** used to scrape or render Claude's TUI output. Claude still runs under tmux (`-L clide`) for process and session persistence per [D-41](#d-41-claude-panes-one-primary-per-repo-tmux-backed), but its *content* is sourced from the transcript file, not from the PTY stream. The terminal emulator (`builtin.terminal`) is retained and fully functional as a general-purpose IDE tool — shell sessions, build output, REPL — but it is not the Claude rendering surface. All transcript parsing is isolated behind a single reader/observer module (`lib/builtin/claude/src/transcript/`) keyed off the transcript `version` field; coupling to Claude Code internal contracts (transcript JSONL schema, `~/.claude/teams/*/config.json`, tmux control mode) is **isolated and version-pinned** (initially claude 2.1.148). **Principle ordering:** Claude-centric first; CLI-first (D-1, D-6) is a strong second. D-6 CLI/event surfaces are preserved where sensible; CLI-first no longer blocks the Claude integration.
|
||||
- **Rationale:** (a) Native Flutter rendering gives cross-widget text selection and copy — the one meaningful advantage of a terminal emulator for plain-text output, recovered in a more flexible form. (b) Removes OS-variant PTY/TUI rendering fragility (xterm escape sequences, tmux passthrough, font-metric alignment) from the Claude display path. (c) It is the substrate for surfacing tmux agent teams as real GUI panels: once the transcript reader exists, each subagent's `.jsonl` file feeds its own panel with no extra IPC. (d) Structured transcript data enables features that scraped terminal text cannot: message-level copy, per-message actions, cost/token annotation, search, diff highlighting. Accepting version-drifting coupling to CC internals is mitigated by isolation + version-pin + fixture tests: the reader module is the only file that knows the JSONL shape; bumping Claude means updating one module and running the fixture suite.
|
||||
- **Cost:** Depends on undocumented and potentially version-drifting Claude Code internals (transcript JSONL schema, subagent path conventions, `~/.claude/teams/*/config.json`). Mitigated by: (1) isolation — all parsing behind `lib/builtin/claude/src/transcript/`, nothing else touches the schema; (2) version-pinning — initial target claude 2.1.148, bumps are deliberate and tested; (3) fixture tests — golden JSONL snapshots at each pinned version exercise the reader. If the schema drifts beyond the reader's tolerance, the Claude pane degrades gracefully (shows a parse-error banner with the raw transcript path) rather than crashing.
|
||||
- **Cross-reference:** [D-1](#d-1-cli-first-not-mcp) (CLI-first preserved as strong second), [D-6](#d-6-cli-and-event-surface-contract) (CLI/event surfaces preserved), [D-41](#d-41-claude-panes-one-primary-per-repo-tmux-backed) (tmux process persistence unchanged), [D-56](#d-56-dissolve-daemon-process-flutter-app-hosts-ipc-server) (Flutter app hosts in-process). Implemented by epic T-132.
|
||||
- **Raised by:** 2026-05-22 — native Claude integration epic (T-132); user set Claude-centric > CLI-first ordering.
|
||||
|
||||
---
|
||||
|
||||
### D-76: ClaudeConfig — Claude's config surface is clide's app settings (builtin-owned, watched, probe-cached per version)
|
||||
- **Date:** 2026-05-23
|
||||
- **Decision:** A single `ClaudeConfig` service in `lib/builtin/claude/` is the app-wide source of truth for Claude Code's environment: skills (`<scope>/skills/*/SKILL.md`), custom slash commands (`<scope>/commands/*.md`), `settings.json`, and permission rules (allow/deny/ask) — read from both the **global** scope (`~/.claude/`) and the **local** repo scope (`.claude/`), layered local-over-global (same ordering discipline as [D-4](#d-4-ignore-file-strategy)'s `ignore_files:`). It exposes typed, listenable views and is refreshed by `FileWatcher` (`lib/src/files/watcher.dart`) on both directories plus an explicit refresh. **Built-in** slash commands (not on disk) are obtained by a one-shot `claude --output-format stream-json` probe whose result is **cached keyed on the resolved claude version id**, so additions/deprecations are re-captured on upgrade; a small static list is the fallback when the probe is unavailable. The service is **builtin-owned, not a kernel service** — Claude is a non-disableable extension but still an extension, so the kernel stays Claude-agnostic and there is one ownership pattern. Consumers (composer slash typeahead, the status pane, later the team/permission surfaces) read from `ClaudeConfig`; none re-scan the filesystem or re-derive the command list.
|
||||
- **Rationale:** (a) Claude is clide's primary citizen, so its on-disk config *is* clide's settings surface — centralizing it removes per-feature filesystem scans and keeps one cache + one watcher. (b) Reuse: the slash list, skills, and permission/model defaults are needed by more than one surface (typeahead + status pane at minimum); a singleton avoids divergent copies. (c) Probe-and-cache-per-version honors "the CLI owns command/skill discovery" (the IDE enumerates from the CLI rather than reimplementing resolution) while bounding the cost of the extra process to once per version. (d) Builtin ownership preserves the kernel/extension boundary — the kernel does not learn about Claude — at the cost of Claude not being a kernel-level "setting", which is acceptable since nothing generic needs it.
|
||||
- **Cost:** Extends the version-drifting CC coupling already accepted in [D-75](#d-75-claude-rendered-natively-from-transcripts-terminal-retained-as-general-tool-only) from the transcript/team schema to the **config layout** (skills/commands dir conventions, `settings.json` shape, permission-rule shape, the stream-json init `slash_commands` payload). Mitigated the same way: all of it lives behind `ClaudeConfig`; the slash-command probe is version-keyed; on a parse miss the service degrades to whatever it could read (and the static built-in fallback) rather than failing. The probe assumes `claude --output-format stream-json` emits an init message listing `slash_commands` — to be confirmed empirically against the pinned version before the typeahead depends on it.
|
||||
- **Cross-reference:** [D-75](#d-75-claude-rendered-natively-from-transcripts-terminal-retained-as-general-tool-only) (native rendering + accepted CC-internals coupling — this extends it to config), [D-1](#d-1-cli-first-not-mcp) (CLI owns command/skill resolution; clide enumerates, never reimplements), [D-4](#d-4-ignore-file-strategy) (global/local layering discipline), [D-3](#d-3-pql-as-supporter-tool-clide-wraps-never-duplicates) (wrap-don't-duplicate analogue for Claude config).
|
||||
- **Raised by:** 2026-05-23 — slash-command typeahead work surfaced that clide has no command/skill enumeration today; user directed a top-level builtin-owned config object reused across surfaces, probe-cached per claude version.
|
||||
|
||||
---
|
||||
|
||||
### D-77: Drive Claude via the stream-json control protocol; teams become a clide-owned coordination layer
|
||||
- **Date:** 2026-05-24
|
||||
- **Status:** accepted — phased (direction confirmed 2026-05-24; phase-1 single-agent first, phase-2 team-coordination scope refined per its own tickets)
|
||||
- **Decision:** Drive the Claude pane through Claude Code's **stream-json control protocol** (`claude --input-format stream-json --output-format stream-json --verbose`, with `--permission-prompt-tool` / SDK-style `canUseTool` handling) instead of running an interactive TUI inside `tmux -L clide`. Conversation content comes from the structured event stream (assistant / tool_use / tool_result / result), not from tailing the transcript JSONL. Session continuity is via `--resume <session-id>` (persistence through the transcript files, not tmux). **Consequence:** Claude Code's built-in experimental **agent-team mode is tmux/interactive-only and not usable headless**, so clide stops *observing* a Claude-run tmux team and instead **orchestrates its own team** — N independent stream-json Claude processes that clide spawns, renders natively, and coordinates through a **clide-hosted MCP server** that supplies the messaging/task tooling Claude's tmux mode provided for free.
|
||||
- **Rationale:** (a) **Closes the interactive-prompt gap** that the TUI model can't: permission requests and **AskUserQuestion** arrive as structured `canUseTool` callbacks (`toolName == 'AskUserQuestion'`, input carries the questions); clide renders a native prompt and returns the answer — exactly the native-rendering control clide wants, with no `capture-pane` text-scraping. (b) **Structured events** replace fragile transcript-file tailing and the "is this the active session?" guessing. (c) **`--resume` persistence** is documented to be identical to the TUI and retires the `--session-id`-already-in-use / session-fork class of bugs (T-156, T-161) and the tmux-session lifecycle entirely. (d) Owning team orchestration is more in line with "own the stack" than wrapping an undocumented, drift-prone tmux feature.
|
||||
- **Team-awareness + messaging (the load-bearing design question):** Claude has no documented way for an independent session to *be* a teammate — the team config/`SendMessage`/task-list are undocumented and coupled to the tmux runtime. So clide manufactures team membership:
|
||||
- **Awareness** — inject roster + role into each agent via `--append-system-prompt` (and/or `--agents <json>`): "you are `<name>`, a teammate on `<team>`; members are …; use the team tools to message them." The agent doesn't *know* it's solo; it's told it's a member and given tools that behave like membership.
|
||||
- **Messaging/tasks** — clide hosts a small **MCP server** (attached to every agent via `--mcp-config`) exposing `send_message(to, text)`, `broadcast(text)`, `list_teammates()`, `inbox()` / a shared `claim_task`/`task_status`. clide is the **broker**: a message from agent A's tool call is delivered into agent B's next turn (as a user/tool message on B's stream-json stdin). The built-in `SendMessage` + auto-delivery + task list do **not** work outside tmux mode, so clide reimplements them — which means clide fully controls routing, ordering, and what the UI shows.
|
||||
- **Orchestration** — there is no SDK/CLI "multi-agent controller" primitive; clide spawns and multiplexes the N processes itself. The existing team UI (T-140 tiles, T-141/T-157 sidebar) is re-pointed from "observe tmux team" to "render clide-managed agents"; the T-139 observer becomes an orchestrator.
|
||||
- **What it unlocks (unified session model):** With clide owning spawn + I/O + the broker, four things that are distinct today collapse into one primitive — a **clide-managed Claude session rendered as a pane**: a teammate, a secondary tab, a forked branch, and an inline subagent become the same thing. Consequences: (a) **the sidebar becomes the cockpit** — the task list and inter-agent messages are local data clide owns, so the sidebar doesn't just display them, it lets the user act (reassign a task, inject/redirect a message, mute/spawn/show/hide an agent); (b) **panel-vs-inline dissolves** — every session is always live, and showing it as a pane is just a visibility toggle on the roster; (c) **fork-into-a-pane** — "branch this conversation" is simply spawning a managed session seeded from another's context (`--resume` + `--fork-session`) through the same plumbing, surfaced as a slash action or a sidebar button. This reframes phase 2 from "reimplement Claude's tmux teams" to "build the unified model the tmux teams only approximated," and is a primary argument *for* the pivot.
|
||||
- **Cost / risk:** Large. It reworks the Claude input/render path (stream-json multiplexer + `canUseTool` prompt UI), replaces D-41's tmux persistence with `--resume`, and rebuilds teams as a clide-owned coordination layer + MCP broker — building messaging/task-sync that the tmux mode gave for free. Undocumented surfaces (team-awareness mechanism, `SendMessage` schema, AskUserQuestion-over-stream-json specifics) mean clide **reimplements rather than wraps**, accepting drift risk isolated behind the orchestrator + MCP server. The interactive terminal builtin remains for general shell use (unchanged). **Mitigation / phasing:** ship the **single-agent pivot first** (stream-json events + `canUseTool` permissions + AskUserQuestion UI + `--resume`), which alone fixes the prompt gap and the session-lifecycle bugs; treat **clide-owned teams** as a separate follow-on epic, decided on its own once the single-agent path is proven.
|
||||
- **Cross-reference:** amends [D-41](#) (tmux-for-persistence → `--resume`; tmux retained only for the general terminal, not Claude), evolves [D-75](#d-75-claude-rendered-natively-from-transcripts-terminal-retained-as-general-tool-only) (native rendering kept, but sourced from the stream-json event stream rather than the transcript file), and supersedes the alternative "keep the tmux TUI + a `capture-pane`/`send-keys` prompt bridge" (rejected as fragile text-scraping that still can't give structured permissions). Relates to [D-1](#d-1-cli-first-not-mcp) (clide now also *hosts* an MCP server for inter-agent tooling), [D-76](#d-76-claudeconfig--claudes-config-surface-is-clides-app-settings-builtin-owned-watched-probe-cached-per-version).
|
||||
- **Raised by:** 2026-05-24 — user testing found the UI cannot handle AskUserQuestion / permission prompts (they live in the TUI, never hit the transcript). Spike (docs + SDK) confirmed stream-json surfaces prompts via `canUseTool` but agent teams are tmux-only; user chose the stream-json direction, accepting that teams must become clide-orchestrated.
|
||||
|
||||
---
|
||||
|
||||
### D-78: Claude permission/prompt transport is the stdio control channel
|
||||
- **Date:** 2026-05-25
|
||||
- **Status:** accepted
|
||||
- **Decision:** Carry Claude's conversation, **permission prompts, and AskUserQuestion over the stream-json stdio control channel** — spawn with `--permission-prompt-tool stdio`, receive `can_use_tool` `control_request`s, reply with a `control_response`. **Do not route these through MCP.** MCP is reserved for the orthogonal job of giving agents *tools/capabilities* (IDE context; the team messaging/task broker per D-77/T-170). This refines D-77's abstract "`canUseTool` handling" into the concrete transport choice.
|
||||
- **Empirically confirmed** (claude **2.1.150**; full shapes + fixtures in [`docs/spikes/cc-stream-json-control-protocol-2.1.150.md`](../../docs/spikes/cc-stream-json-control-protocol-2.1.150.md)): `stdio` is **mandatory** — without it, "ask" tools are silently **auto-denied** and no prompt reaches the client; an `allow` decision must echo back `updatedInput` (a bare `{behavior:"allow"}` is rejected); `deny` requires `message`; **AskUserQuestion is permission-gated through the same `can_use_tool` channel** and answered by injecting `updatedInput.answers` (question-text → chosen label), *not* via a `tool_result`.
|
||||
- **Rationale:** Directness — no extra process or loopback "networking", and Claude hands us structured prompt metadata (`display_name` / `description` / `permission_suggestions`) for free. It is exactly what the SDK's `canUseTool` maps to. Routing permissions through MCP instead is strictly more machinery (the broker must be up before the first prompt; the structured fields must be hand-rolled) for no gain.
|
||||
- **Cost / risk:** The control channel is an **undocumented internal contract pinned to a claude version** — Anthropic may change or remove it on any bump. Mitigation: pin `claude_code_version`; the captured spike logs serve as **regression fixtures / canaries**; all protocol framing lives behind one module (per D-77) so the transport is a one-seam swap. **Documented fallback menu** so a shift never lands us at a blank slate (preference order): (1) **MCP permission tool** — `--permission-prompt-tool mcp__clide__approve` against the T-170 broker (strongest; MCP is a public/stable protocol and the broker will already exist); (2) degrade to `--permission-mode acceptEdits` / `dontAsk` / `bypassPermissions` as a reduced-fidelity stopgap; (3) the **Agent SDK** if a stable public `canUseTool` surface ships; (4) **ACP** (`session/request_permission`) if the ecosystem converges on it. Detection symptoms + detail in the spike doc.
|
||||
- **Cross-reference:** refines [D-77](#d-77-drive-claude-via-the-stream-json-control-protocol-teams-become-a-clide-owned-coordination-layer); relates to [D-1](#d-1-cli-first-not-mcp) (clide hosts MCP for *capabilities*, never for the conversation/permission transport) and [D-76](#d-76-claudeconfig--claudes-config-surface-is-clides-app-settings-builtin-owned-watched-probe-cached-per-version). Implemented by T-166.
|
||||
- **Raised by:** 2026-05-25 — during T-165/T-166 work, an empirical spike against claude 2.1.150 (driving the real CLI + reading the shipped binary's zod schemas) nailed the control-protocol shapes. User weighed stdio vs MCP for permissions, chose stdio for directness, and asked that the brittleness and researched alternatives be documented so a future Anthropic change doesn't leave clide without options.
|
||||
|
||||
---
|
||||
|
||||
### D-79: Workspace content search is a pure-Dart in-process engine, outside pql
|
||||
- **Date:** 2026-05-31
|
||||
- **Status:** accepted
|
||||
- **Decision:** Find-in-files / search-and-replace (T-52/T-53) run as a **pure-Dart, in-process grep engine** — an isolate worker pool fans non-ignored files out across cores, matches with `RegExp` (with a literal `indexOf` fast-path when the regex toggle is off), and **streams** matches back over a single engine-agnostic IPC verb (`search.grep`) with cancellation when the query changes. It does **not** shell out to `ripgrep`/`grep`, and it does **not** route through pql.
|
||||
- **Rationale / D-3 boundary:** D-3 says clide *wraps pql for query surfaces* — but `pql search` is a **ranked full-text *document* index** (returns `path`/`score`/`connections`, no line numbers, snippets, regex, case, or glob). Content-grep ("match-in-context, click-to-line, regex/case/glob") is a **code-navigation primitive pql does not offer**, so it is explicitly outside pql's query surface and is clide's to own (consistent with the "own the rendering/tooling stack" guardrail). A shell-out to `ripgrep` was rejected as the default: it adds an unvendored external binary that isn't guaranteed present (esp. cross-platform), against prefer-zero-deps and single-process.
|
||||
- **Performance:** the I/O floor (walk + read) is shared by every engine. ripgrep's edge is multithreading + SIMD literal prefilters + a non-backtracking DFA; the Dart engine recovers the dominant win (parallelism) via an **isolate pool**, sidesteps the regex-engine gap for the common case via the **literal fast-path**, and hides the rest behind **streaming + cancellation**. Net: interactive (sub-second) on realistic repos including this one; ripgrep only pulls visibly ahead at monorepo scale clide is not targeting.
|
||||
- **Escape hatch (de-risk):** the `search.grep` request/result contract is **engine-agnostic**. If a giant-repo benchmark ever demands it, an optional "use `rg` when on PATH, else the built-in engine" accelerator can slot in behind the same verb with **no caller changes** — recorded as future work on T-52, not built now.
|
||||
- **Distinct from structural search:** this is *text* grep. *Structural/semantic* search ("find usages", "go to definition") is tree-sitter's job (clide already vendors `libtree-sitter.so` for highlighting) and is a separate future feature, not part of T-52.
|
||||
- **Cross-reference:** clarifies [D-3](#) (pql wrap boundary) and the "own the stack" guardrail; relates to [D-4](#d-4-ignore-file-strategy) (the engine honors the full `ignore_files:` layering — see T-52, which closes the never-filed ignore-layering placeholder, a `(to be recorded)` comment in `files_commands.dart`). Implemented by T-52 (engine + find-in-files) and T-53 (replace).
|
||||
- **Raised by:** 2026-05-31 — during /whats-next refinement of the search/nav batch (T-51/T-52/T-53). Refinement surfaced that `pql search` structurally can't satisfy find-in-files; the user probed tree-sitter (ruled out — it's a parser, not a grepper) and the performance ceiling, then chose the fastest *reasonable* Dart option (isolate pool + literal fast-path + streaming) over a ripgrep dependency.
|
||||
|
||||
---
|
||||
|
||||
### D-80: `files.read` allows trusted Claude config roots beyond the workspace
|
||||
- **Date:** 2026-06-01
|
||||
- **Status:** accepted
|
||||
- **Decision:** `files.read` accepts an **allow-list of read roots**: the workspace root (as before) **plus** the resolved Claude config directories — the global `~/.claude` and the repo-local `<repo>/.claude` (the latter already lives under the workspace). A path is readable when it is contained by **any** allowed root; everything else is still rejected with `path outside workspace`. This widens **reads only** — writes (`search.replace`, future `files.write`) stay confined to the workspace root.
|
||||
- **Rationale:** Per [D-76](#d-76-claudeconfig--claudes-config-surface-is-clides-app-settings-builtin-owned-watched-probe-cached-per-version) Claude's config surface (skills/agents/commands under `~/.claude` + the repo `.claude`) is clide-managed and surfaced in the Config tab. Opening a surfaced skill's `SKILL.md` in the markdown reader is a legitimate, expected action, but `~/.claude` is global and outside the repo — the original [T-102] confinement rejected it (`path outside workspace`). Extending the allow-list to exactly the config roots the app already reads is the user's chosen model ("the `.claude` dir is in the workspace") and is simpler than a separate trusted-read verb.
|
||||
- **Security boundary:** This is a *bounded* widening, not a hole. Only the explicitly-listed config roots are added; arbitrary off-repo paths and `..` traversal are still rejected, and the symlink re-check ([resolveUnderRootsFollowingSymlinks]) re-verifies the real path is contained by one of the allowed roots (so a symlink under a config root pointing to `/etc/shadow` is still refused). The roots are the user's own trusted Claude config (same trust level as pql's data per D-3), and writes are unaffected.
|
||||
- **Cross-reference:** amends the read side of [D-4](#d-4-ignore-file-strategy)/T-102's "repo-is-the-workspace" confinement; builds on [T-194](#) (absolute-under-root reads). Implemented by T-195 — `FilesService.extraReadRoots`, wired in `main.dart` to `~/.claude` when present.
|
||||
- **Raised by:** 2026-06-01 — after T-194 fixed repo-scope skill reads, the user hit `path outside workspace` opening a *user-scope* skill (`~/.claude/skills/peon-ping-toggle/SKILL.md`) and said the `.claude` dir should be in the workspace. Chose extending the read allow-list over a separate trusted-read verb.
|
||||
|
||||
---
|
||||
|
||||
### D-81: Right-pane reader load is driven by a retained `ReaderNav`, not per-view state or bus retention
|
||||
- **Date:** 2026-06-01
|
||||
- **Status:** accepted
|
||||
- **Decision:** The right-pane readers (markdown, decisions) load content from a **retained per-reader `ReaderNav`** (a kernel `ChangeNotifier` held in a `ReaderNavRegistry`), not from per-view `State` and not from a "retained/replay-latest" MessageBus channel. `ReaderNav` owns the back/forward history + pin, subscribes to its reader's `selection` channel to record entries, and **(re-)emits `load`** — the single channel a reader displays from. A reader **grabs `nav.current` on mount** (so a selection that revealed its tab before it subscribed isn't lost), and loads from `load` while mounted. Back/forward/pin navigate the nav and re-emit `load`, so every load flows through one path.
|
||||
- **Rationale:** The bug (T-196): a reader subscribes in `didChangeDependencies`, which runs only *after* the tab is revealed, so the broadcast `selection` that triggered the reveal is already gone — first click did nothing. A post-frame re-publish "fixed" it but is timing-fragile (hard to test deterministically). Two clean options remained: make the MessageBus retain-and-replay the last value to new subscribers, or hold the retained state in the nav-history helper. **The nav-history is the better home** — the reader's "what am I showing" *is* its current history entry, the helper already exists for back/forward, and it keeps the MessageBus a dumb pipe (no per-channel retention semantics, no unbounded memory question). Grab-on-mount is a pull; the bus stays push-only.
|
||||
- **Cost / alternatives:** Rejected **bus retention** (BehaviorSubject-style replay) as the wrong layer — it would bake stateful replay into every channel and surprise non-reader subscribers. Rejected **per-view `ReaderHistory`** (the old `ReaderHistoryMixin`, now deleted) because it dies with the widget, which is the whole bug. The cost is a small kernel service (`ReaderNavRegistry`) on `KernelServices` + the extension context.
|
||||
- **Cross-reference:** fixes T-196; the readers reveal their tab on `selection` (extension) then pull `nav.current`. Relates to [D-78](#d-78-claude-permissionprompt-transport-is-the-stdio-control-channel)-era reader work (T-187/188/189). Implemented in `lib/kernel/src/reader_nav.dart` + the markdown/decisions readers.
|
||||
- **Raised by:** 2026-06-01 — the user reported decisions opening only on the second click, diagnosed the lost-on-mount race, and explicitly chose a "right-pane nav history helper" over a MessageBus fix ("leaving them in the messagebus is the wrong shape").
|
||||
|
||||
---
|
||||
|
||||
### D-82: Keymap sequences are space-separated; matching is a reusable matcher consumed at the interception point
|
||||
- **Date:** 2026-06-01
|
||||
- **Status:** accepted
|
||||
- **Decision:** A keymap binding's `keys:` may be a **multi-chord sequence** written as a **space-separated string** (`'d d'`, `'g g'`, `'ctrl+k ctrl+s'`): space means "then". The existing forms are unchanged — `+` joins modifiers within one chord, and a YAML **list** (`[ctrl+p, meta+p]`) still means **alternation** ("or"). A leading digit run in normal mode is captured as a **repeat count** and applied by firing the resolved intent N times (not threaded into the intent payload). Sequence *matching* is a reusable two-part facility: `Keymap` answers a **stateless** prefix/exact/none query over its bindings, and a small **stateful `SequenceMatcher`** (pending buffer + count + timeout) wraps it. **Interception lives at the consumer**, not in the global key handler.
|
||||
- **Rationale:** Real Vim needs `dd`, `gg`, `dw`, `ciw`, `5j` — impossible under single-chord resolution (T-205). Of the candidate separators, every punctuation option (`,` `;` `>`) is *itself a bindable key* (Vim leader, repeat-find, indent), so each would force an escape rule (`\>`). A literal **space never appears as a key spec** — the space key is always spelled `space` — so it separates with zero collisions and no escaping, and matches Vim-doc / VS Code convention (`ctrl+k ctrl+s`). Count-by-repeat keeps intents `const` and payload-free. The interception split is forced by the host: the global dispatch is a passive `KeyboardListener` (returns void, **cannot swallow** events), so normal-mode keys can't be intercepted there before `EditableText` types them — the editor's `Focus.onKeyEvent` (returns `KeyEventResult`) is the only place that can consume them. Putting the matcher there (T-206) keeps T-205 pure, headless, and unit-testable, and avoids the global handler buffering keys it has no power to swallow.
|
||||
- **Cost / alternatives:** Rejected a **full Vim grammar engine** (operator × motion × text-object × count combinatorics) for the first pass — common operator+motion combos are enumerated as explicit sequence bindings in `vim.yaml` instead, covering the demo surface without a parser. Rejected **comma/`>`/semicolon separators** (escape wart). Rejected **count-in-intent-payload** (would de-`const` every intent and bloat the bridge). Rejected **buffering in the global `resolveEvent`** (it can't swallow, so it would double-handle with the editor). Cost: `KeymapBinding` generalises `chord` → an ordered chord list; a new `SequenceMatcher`; the editor owns interception.
|
||||
- **Cross-reference:** T-205 (matcher + notation), T-206 (editor interception + motions), T-65 (`vim.yaml`). Builds on the T-117 keymap layer; `vim.*` scope flags from [D-81](#d-81-right-pane-reader-load-is-driven-by-a-retained-readernav-not-per-view-state-or-bus-retention)-era work are set by the Vim mode service (T-207).
|
||||
- **Raised by:** 2026-06-01 — scoping the Vim preset for a Vim-power-user demo; the user weighed `,`/`;`/`>` separators and flagged the escaping problem, which made space the collision-free choice.
|
||||
|
||||
---
|
||||
|
||||
### D-83: Dogfood agent model — hosted stream-json session primary, external CLI driver secondary
|
||||
- **Date:** 2026-06-03
|
||||
- **Status:** accepted
|
||||
- **Decision:** There are two distinct "Claude inside clide" agents, and clide commits to **both** with an explicit primary. **(A) The clide-hosted stream-json session is the primary dogfood target.** Per [D-77](#d-77-drive-claude-via-the-stream-json-control-protocol-teams-become-a-clide-owned-coordination-layer)/[D-78](#d-78-claude-permissionprompt-transport-is-the-stdio-control-channel) clide *spawns* the Claude process, so it owns that child's environment — this is the agent clide can fully **bootstrap** (inject `CLIDE_SOCK`/`CLIDE_WORKSPACE`, guarantee `clide` on its PATH, inject a context note, seed a `Bash(clide *)` allow rule — Epic B / T-214) and the agent the rest of the product is built around. **(B) An external Claude Code harness driving via `clide …` is a first-class but secondary, best-effort integration** — the same surface [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) already opens to "human shell use, scripts, and external editor integrations." clide serves the per-workspace socket ([D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic)) to anyone who can reach it and offers a manual install affordance (T-212), but makes **no promise to observe an external agent's non-`clide` tool use** (plain file reads, `make test`, `git`) — those bypass the socket and are outside clide's view by construction.
|
||||
- **Parity scope (how this frames Epic C):** the [D-6](#d-6-cli-and-event-surface-contract) "agent sees what the user sees" contract is scoped to **clide's own surfaces, reflected through the CLI in both directions** — the live UI panes/buffers/active-file become readable via `pane list` / `editor active` / `clide status` (T-219/T-220/T-221), and an external agent's `clide …` mutations are observable on the event bus. An external agent's side-channel tool use (reads/tests/git issued in its own shell) is **explicitly out of parity scope**: clide reflects what flows through clide, not what an unowned process does elsewhere.
|
||||
- **Rationale:** (a) clide can only *bootstrap* a process whose launch it controls — env, PATH, permission rules, and context injection are all things you set when you `spawn`, not things you can push into a shell you didn't start; so the hosted session (A) is where Epic B's leverage actually exists, which makes it the right primary. (b) Naming (A) primary aligns the dogfood target with the product's main interaction surface (the native Claude pane, D-77). (c) Refusing to drop (B) keeps faith with D-68 (the CLI is deliberately an external-integration surface) and with reality — the self-analysis probe and this very initiative are being driven by an external harness; declaring it out of scope would make clide unable to describe its own development. (d) Scoping parity to "what flows through clide" keeps the contract **honest and testable**: clide can verify it mirrors its own UI state, but cannot truthfully promise to observe tool calls that never touch its socket.
|
||||
- **Cost / risk:** Two supported models means two bootstrap stories. Mitigated by the asymmetry being the point — (A) gets the full automated bootstrap; (B) gets a documented manual path (install `clide`, the deterministic socket resolves the rest) and best-effort observability, not a second full implementation. The honest limit on (B) — clide is blind to an external agent's non-`clide` work — is a stated boundary, not a bug to chase; an external agent that wants clide to see an action runs it *through* `clide …`.
|
||||
- **Cross-reference:** answers Gap 5 of [`docs/self-analysis.md`](../../docs/self-analysis.md); gates [D-77](#d-77-drive-claude-via-the-stream-json-control-protocol-teams-become-a-clide-owned-coordination-layer)/[D-78](#d-78-claude-permissionprompt-transport-is-the-stdio-control-channel) (the hosted session this names primary), [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) (the external CLI surface kept as secondary), [D-6](#d-6-cli-and-event-surface-contract) (parity scoped here), [D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic) (the socket an external agent reaches). Frames Epic B ([T-214](#)) bootstrap target and Epic C ([T-218](#)) observability scope under initiative [T-208](#) "Give Claude hands". Implemented/decided by T-224.
|
||||
- **Raised by:** 2026-06-03 — T-224, processing the self-analysis.md dogfood probe from inside clide. The probe itself ran as an external harness (B) while the product is built around the hosted session (A); user chose "both, primary = hosted (A)" so Epic B optimizes the bootstrap clide can actually control while keeping the external driver a supported, best-effort integration.
|
||||
|
||||
### D-84: Diff view placement — editor-mode inline above Claude, spawned from the git sidebar
|
||||
- **Date:** 2026-06-05
|
||||
- **Status:** accepted
|
||||
- **Decision:** The diff view is **not** a workspace tab (its current placement violates [D-48](#d-48-chrome-budget-no-tabs-no-breadcrumbs-keyboard-first) "no buffer/workspace tabs"). It opens as an **editor-mode surface** — lifting *above* Claude in the middle column, the same mechanism as the inline editor ([D-49](#d-49-editor-mode-inline-above-claude-viewer-swap)) — and is **spawned from the git sidebar** (selecting a changed file opens its diff there). It is **not** a context-panel viewer.
|
||||
- **Rationale:** A diff is a review *and intervention* surface, not just passive inspection: to be fully functional it must host hunk-level stage/discard and (later) conflict-resolution widgets, which need horizontal room. The context panel (≈420px, [D-50](#d-50-context-auto-behavior-right-panel-reacts-to-claude) viewer semantics) is too narrow to comfortably hold those controls; the middle column above Claude gives the space while keeping Claude's prompt bar fixed ([D-47](#d-47-interaction-model-claude-is-home-layout)). Spawning from the git sidebar matches the user's mental model (left = change list, the diff opens in the work area). This also partially serves the side-by-side-compare desire behind [Q-27](../questions/architecture.md#q-27-two-editor-split) without a true editor split.
|
||||
- **Cost / risk:** The editor-mode surface currently hosts the single inline editor; it must generalize to host a diff too (editor and diff are mutually exclusive in that slot, like the viewer↔editor swap in D-49). Implementation is a follow-up story, not part of this decision ticket (T-42).
|
||||
- **Cross-reference:** [D-48](#d-48-chrome-budget-no-tabs-no-breadcrumbs-keyboard-first) (no workspace tabs — the rule the old placement broke), [D-49](#d-49-editor-mode-inline-above-claude-viewer-swap) (the editor-mode mechanism reused), [D-50](#d-50-context-auto-behavior-right-panel-reacts-to-claude) (the viewer model explicitly rejected here), [D-47](#d-47-interaction-model-claude-is-home-layout) (prompt bar fixed), [Q-27](../questions/architecture.md#q-27-two-editor-split) (partially served). Decides T-42.
|
||||
- **Raised by:** 2026-06-05 — T-42 (decide diff view placement). User chose editor-mode over the context-panel viewer: a diff needs space for resolution widgets and spawns from the git sidebar, so it's too cramped to compress into the right panel.
|
||||
|
||||
---
|
||||
|
||||
### D-85: Event bus delivery — bounded ring-buffer back-pressure; in-memory cursor retention, bus-owned if persisted
|
||||
- **Date:** 2026-06-06
|
||||
- **Status:** accepted
|
||||
- **Decision:** The in-memory event bus ([D-6](#d-6-cli-and-event-surface-contract)) gets explicit delivery + retention semantics, resolving [Q-2](../questions/architecture.md#q-2-back-pressure-on-event-streams) (back-pressure) and [Q-3](../questions/architecture.md#q-3-event-persistence--auditundo) (persistence). **(1) Back-pressure — bounded per-subscriber ring buffer, drop-oldest.** Each subscriber gets a bounded ring; when a slow or absent reader fills it, the oldest events are dropped and a per-subscriber dropped-count is incremented. The producer never blocks and subscribers are never force-disconnected. **(2) Cursor retention — in-memory, monotonic cursor.** The pull API (`clide events --since <cursor>`, T-223) is served from a bounded in-memory ring keyed by a monotonically increasing cursor; `--since` returns the events after the cursor plus a next-cursor, and reports a **gap marker** (the dropped-count) when the requested cursor has already aged out of the ring — so a caller detects loss instead of silently missing events. No on-disk persistence in v1. **(3) If persistence is ever needed** (audit log, undo history) it is **owned by the bus itself**, not a separate subscriber-subsystem. ADR 0006 carried the opposite as a bare one-line position in its *open-questions* footer ("a subsystem that subscribes and writes — not a property of the bus") with **no rationale** — which is exactly why it migrated to Q-3 rather than a decision; this resolves that open question the other way, on the reasoning below.
|
||||
- **Rationale:** drop-oldest plus a visible gap marker is the only policy that keeps an interactive IDE responsive — no producer stall from a wedged reader (the main-isolate dispatch of [D-72](#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate) must never block on a subscriber) — while still letting a request/response agent loop *know* it fell behind. Blocking the producer risks janking the PTY/UI; killing subscribers pushes resync onto every client. The ring doubles as the cursor store, so back-pressure and pull-retention are one mechanism, not two. Bus-owned persistence (if it ever lands) keeps a single authoritative ordering + cursor space; a side-subscriber writer would have to reconstruct ordering and could itself fall behind under the very back-pressure policy this decides.
|
||||
- **Cost / risk:** ring size is a tuning knob — too small and slow agents see frequent gaps, too large and memory grows under a firehose; sized per channel and revisited if a real consumer hits gaps (the Q-2 triage trigger). A caller that ignores the gap marker silently misses events — mitigated by making next-cursor + gap explicit in the `--since` response shape. Naming bus-owned persistence as the eventual shape pre-commits against the subscriber-writer pattern; acceptable because nothing persists yet and the call is revisitable when the first real audit/undo requirement lands.
|
||||
- **Cross-reference:** [D-6](#d-6-cli-and-event-surface-contract) (the event surface), [D-72](#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate) (serial main-isolate dispatch the producer must not block), [Q-2](../questions/architecture.md#q-2-back-pressure-on-event-streams) + [Q-3](../questions/architecture.md#q-3-event-persistence--auditundo) (resolved here). Unblocks T-223 (cursor pull events); amends ADR 0006 (persistence lean).
|
||||
- **Raised by:** 2026-06-06 — walking Q-2/Q-3 during the T-208 "give Claude hands" wind-down; user chose the drop-oldest ring buffer, in-memory retention now, and bus-owned persistence if it ever becomes durable.
|
||||
|
||||
---
|
||||
|
||||
### D-86: MCP tool surface — full clide namespace generated from the co-registered command registry
|
||||
- **Date:** 2026-06-06
|
||||
- **Status:** accepted
|
||||
- **Decision:** Resolves [Q-32](../questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools). clide exposes the **full `mcp__clide__*` tool namespace** — not just the `/ide` minimum (`getDiagnostics` + `executeCode`) — but **every surface (CLI argv, MCP `tools/list`, command palette) is generated from the one co-registered command registry** established by [D-74](#d-74-ipc-command-schema-is-co-registered-with-the-handler-validated-at-dispatch), never hand-authored per-surface. A command registers once (handler + typed arg schema); the MCP adapter derives its tool definition (name, description, JSON-Schema input) from that same registry entry, exactly as the CLI argv grammar and palette entry already do. There is no separately-maintained MCP tool list. A registry entry may carry an **MCP opt-out flag** so a command that is a poor tool (long-lived streams, UI-side-effecting verbs) can register without exposing a twin. This extends [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) (which deferred the `mcp__clide__*` namespace to Q-32) and consumes the registry whose MCP generation D-74 had marked deferred to the T-130 track.
|
||||
- **Rationale:** the maintenance objection to a broad MCP surface (D-68's "every CLI verb invites an MCP twin; surface bloat") only bites if the twin is authored by hand. Generated from the registry, breadth is nearly free: the same source of truth that already feeds CLI + palette feeds MCP, so adding a command lights up all three surfaces at once and they cannot drift. The extensions-first model ([D-46](extensions.md#d-46-core-frame-builtins-vs-shipped-extensions-boundary)) requires registry-driven surfacing anyway — extension-contributed commands must appear in every surface without the core editing a central list. Full breadth makes clide a real backend for non-Claude-Code MCP clients (Cursor, Windsurf, Copilot), the growing `/ide` ecosystem D-68 already says clide should be a peer in. Claude-in-a-clide-pane still uses the CLI; the MCP breadth is for *external* clients.
|
||||
- **Cost / risk:** the adapter must map D-74's arg-schema vocabulary to MCP's JSON-Schema tool-input shape — one adapter, written once, not per command. Exposing the full action surface to any connected MCP client widens what a client can drive — gated by the same transport boundary as today (MCP reaches the dispatcher over the SSE transport of [D-73](#d-73-mcp-transport-for-ide-is-sse-over-http); no new auth surface, same dispatcher as the socket). More tools in `tools/list` is more for a client to reason about; the opt-out flag + good per-command descriptions keep it sane.
|
||||
- **Cross-reference:** [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) (dual surface; deferred `mcp__clide__*` to Q-32 — resolved here), [D-74](#d-74-ipc-command-schema-is-co-registered-with-the-handler-validated-at-dispatch) (the co-registered command+schema registry this generates from), [D-73](#d-73-mcp-transport-for-ide-is-sse-over-http) (SSE transport it serves over — unchanged; Q-33 stays closed by D-73), [D-46](extensions.md#d-46-core-frame-builtins-vs-shipped-extensions-boundary) (extensions-first, which already requires registry-driven surfacing), [D-6](#d-6-cli-and-event-surface-contract) (CLI/parity the registry enforces). Defines the tool surface for T-225. Resolves [Q-32](../questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools).
|
||||
- **Raised by:** 2026-06-06 — walking Q-32 during the T-208 wind-down; user chose the full namespace but constrained it to single-registry generation to avoid a second maintenance front, noting the extensions-first pattern needs that anyway.
|
||||
|
||||
---
|
||||
|
||||
### D-87: Output/log dock — bottom, toggled, read-only (logs + problems)
|
||||
- **Date:** 2026-06-06
|
||||
- **Status:** accepted
|
||||
- **Decision:** clide gains a **bottom output dock** — a toggled, **read-only** panel hosting two tabs: **Output** (the kernel log stream) and **Problems** (diagnostics). Resolves [Q-28](../questions/architecture.md#q-28-terminal-strip-scope--shell-only-or-logserrorstests): the bottom strip hosts logs/problems, **not** the terminal.
|
||||
- **Toggle = one merged status-bar widget.** Clicking it (or `⌘J` / `Ctrl+J`) opens/closes the dock. The widget **replaces the separate app-status indicator** rather than adding a segment (chrome budget, [D-48](#d-48-chrome-budget--no-tabs-no-breadcrumbs-keyboard-first)): it shows green `✓` when clean, `⚠`/`✕` diagnostic counts when not, and a `▲`/`▼` chevron for dock state. The badge means problem counts are visible **without** opening the dock.
|
||||
- **Output tab:** the `Logger` stream (subsystems: ipc, mcp, extensions, pql, git, pane, …), filterable by **source + level + text**, auto-scrolling (follow tail; scrolling up pauses follow and shows "jump to latest").
|
||||
- **Problems tab:** diagnostics, **moved out of the left sidebar** — no duplication, and the dock's full width suits `severity · file:line · message` rows the 180–400px sidebar truncated. The status badge replaces the sidebar panel's always-visible glance.
|
||||
- **Retention:** add a bounded in-memory **ring sink** to the `Logger` (drop-oldest, fixed cap). The logger today only live-broadcasts with no history, so late openers would see nothing; the ring serves "the last N records" on open. Same drop-oldest shape as the [D-85](#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) event ring.
|
||||
- **Layout:** the dock occupies the bottom of the workspace column and pushes Claude **up**; the prompt bar rides up with it. Height is resizable (top edge) and open/closed + height persist per workspace. This is an explicit exception to [D-47](#d-47-interaction-model--claude-is-home-layout) rule (1) — see its amendment.
|
||||
- **Rationale:** Q-28 asked whether the bottom strip should host logs/errors/tests. The answer splits **by interaction**: the dock is **passive** (you skim it) so it earns the always-present-but-collapsed bottom slot; the **terminal is active** (you work in it) so it stays a first-class surface in the editor pane ([D-49](#d-49-editor-mode--inline-above-claude-viewer-swap)), never demoted into a log strip. Merging health into the toggle widget delivers a new capability with **zero new persistent chrome** and keeps the at-a-glance count alive while the dock is closed. Problems moves in because the width fits and the badge covers the glance it used to provide from the sidebar.
|
||||
- **Cost / risk:** Breaks D-47's literal prompt-bar Y-invariance — mitigated by the explicit amendment, the ≥50% cap (Claude stays the largest surface), and the dock only moving on a deliberate user toggle. Adds bounded log-buffer memory. Relocating Problems costs muscle memory + the slot move. Small-screen behaviour interacts with [Q-26](../questions/architecture.md#q-26-small-screen-layout--1000px): under a short window the dock + ≥50% cap may force a smaller dock or a modal — deferred to Q-26.
|
||||
- **Cross-reference:** [D-47](#d-47-interaction-model--claude-is-home-layout) (amended — prompt-Y exception), [D-48](#d-48-chrome-budget--no-tabs-no-breadcrumbs-keyboard-first) (merged widget adds no segment), [D-49](#d-49-editor-mode--inline-above-claude-viewer-swap) (where the terminal lives instead), [D-51](#d-51-panel-collapse--12px-spine-with-badge) (collapse-with-badge pattern the toggle echoes), [D-85](#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) (drop-oldest ring reused), [Q-26](../questions/architecture.md#q-26-small-screen-layout--1000px). Resolves [Q-28](../questions/architecture.md#q-28-terminal-strip-scope--shell-only-or-logserrorstests). Implemented by T-54; the terminal's editor-pane home is tracked separately.
|
||||
- **Raised by:** 2026-06-06 — T-54 UX design session (Frame0 wireframe under `docs/design/wireframes/output-dock/`). User chose a status-bar-toggled bottom dock scoped to read-only output, Problems folded in, terminal kept first-class in the editor pane.
|
||||
|
||||
---
|
||||
|
||||
### D-90: clide:// deep links — paranoid allowlist + user confirmation
|
||||
- **Date:** 2026-06-09
|
||||
- **Decision:** The `clide://` URL scheme (T-56) is treated as an UNTRUSTED external vector. A clide:// link is NOT translated into a command in the CLI parser; `parseArgv` hands the raw URL to a `deeplink.invoke` handler that is doubly defensive: (1) **default-deny allowlist** — only the actions in `kDeepLinkSafeActions` (today just `open`, a read-only navigation verb) are even parseable; anything else (`run`, `git`, `write`, arbitrary command passthrough, non-`clide` schemes) is rejected outright; and (2) **mandatory user confirmation** — every allowlisted action shows a "an external link wants to: … allow?" modal, framed as untrusted, before it executes. The OS scheme registration (linux `.desktop` `x-scheme-handler/clide`, macOS `CFBundleURLTypes`) + the CLI→IPC route mean a link lands in the running window (single-instance).
|
||||
- **Rationale:** The `clide` CLI is a *local, trusted* surface (D-1/D-6); a URL handler is the opposite — any webpage can fire `clide://…` at the OS. A generic passthrough to the full command surface would be maximally extensible but would turn a malicious link into a remote control for the IDE (trigger `git push`, file writes, session kills). Paranoid-allowlist + confirm keeps the useful "open this file at this line from a CI link / error report" case while making the dangerous surface unreachable. The allowlist is the security boundary and is unit-tested in isolation.
|
||||
- **Cost:** Each new deep-link action is an explicit, reviewed addition to `kDeepLinkSafeActions` + its handler branch — extensibility is deliberately gated, not free. The confirmation prompt adds a click to every deep-link open (acceptable for an out-of-band entry point). macOS URL *delivery* (AppDelegate `openURLs` → Dart) is a separate follow-up (T-303); the security model applies once delivery lands.
|
||||
- **Raised by:** 2026-06-09 — user, on reviewing the T-56 passthrough: "put a heavy blocklist with do-you-want-this prompts on it (a paranoid allowlist)." Spotted that routing clide:// through the CLI path is "incredibly extensible" and that extensibility is exactly the risk for an external vector.
|
||||
|
||||
---
|
||||
|
||||
### D-91: Unified conversation drawing card backed by a canvas renderer
|
||||
- **Date:** 2026-06-10
|
||||
- **Decision:** The conversation pane gets ONE drawing card — a clide-owned canvas renderer driven by a JSON document — instead of a growing set of one-off renderers (image card, icon card, …). The mental model is the **HTML `<canvas>`**, not Obsidian's `.canvas` (Obsidian naming its feature "canvas" does not make its schema our pattern). The card's JSON can express raw **primitives** (rects/lines/text/etc. at coordinates — "draw a box at x,y, a line from x,y to x,y" works), but in the common case it runs in **template mode**: the JSON names a predefined component and the card grabs it — `image` (display + lightbox-on-click), `icon` (the multi-size glyph set, D-78/T-313), `compare-images` / before-after (two paths side by side), and — added incrementally — `svg`, `graph`, and more. Every drawn object may carry an optional **label + description**, rendered as a widget beneath it only when those fields are present in the JSON. Templates lower onto the same primitive scene the raw API accepts (hybrid: scene-graph core + high-level block sugar). The card stays **display-only** per [D-78] — selection/interaction happens in the interaction zone, not on the card. The same renderer is intended for **reuse as the `.canvas` viewer** elsewhere: a `.canvas` file is converted into this JSON rather than the card adopting Obsidian's schema natively. The already-shipped image card (T-249/T-252) is left in place for now and migrated onto this canvas once the canvas layer lands.
|
||||
- **Rationale:** Each new "show X in the conversation" need was trending toward another bespoke message type + widget + CLI verb. A single canvas that dispatches templates (and falls back to primitives) collapses that into one surface to build, test, and drive — new visuals become a new template/JSON shape, not a new renderer. Anchoring on HTML `<canvas>` rather than `.canvas` keeps the model general (arbitrary drawing) instead of bending features to a third-party document schema, while still letting us ingest `.canvas` via conversion. Hybrid (templates over primitives) is the pragmatic middle: the common cards ship without hand-placing coordinates, but nothing is locked out of the primitive layer. Honors "own the rendering stack" — canvas is explicitly a clide-owned `CustomPaint`, not a packaged widget.
|
||||
- **Cost:** A real renderer with a JSON schema is more up-front design than another one-off widget, and the schema must stay coherent across the primitive and template layers as templates accrue. Templates expand over time ("we will expand the templates as we go") so the schema is deliberately open-ended; that flexibility is a standing maintenance surface. The `.canvas`→JSON converter is additional scope, deferred. Migrating the shipped image card is follow-up churn on working code.
|
||||
- **Relationship:** Narrows [Q-4](../questions/architecture.md#q-4-canvas-schema-compatibility-with-obsidian) — clide's canvas is its own HTML-canvas-inspired JSON; Obsidian `.canvas` is an *import* format via conversion, not the native schema. Consumes the stdin/`--file` JSON input plumbing (T-315). Subsumes the standalone icon card (T-313) and image-annotation work (T-316) as templates of this card. **Merges the former Tier-5 "canvas and graph view" epic (T-7) into one canvas epic (T-317):** the Tier-5 canvas *pane* (T-322, interactive/editable — distinct from the display-only conversation card) and graph *view* (T-323) consume the same shared renderer; T-7 is cancelled as superseded. The conversation drawing card stays display-only per [D-78]; the canvas pane is a full interactive pane. (D-17 "panels are extension-shaped" is unaffected and still governs the panes.)
|
||||
- **Raised by:** 2026-06-10 — user, while refining the icon-preview card (T-313): "make it all into one drawing card that receives a json input and selects based on the context inside the json what to draw … pull the entire thing closer to a dynamic canvas than a bunch of one-off renderers." Clarified the model is HTML `<canvas>` (not Obsidian's), templates-over-primitives, per-object label/description, and reuse as the `.canvas` renderer; before/after comparisons, SVGs, icons, and graphs all become things you send into the card.
|
||||
|
||||
---
|
||||
|
||||