Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
34847cad58 chore: rename solidhaus → kammer
Some checks failed
Build and Push OCI Image / build (push) Failing after 13s
- Update flake outputs: solidhaus-image → kammer-image
- Update registry references: registry.toph.so/solidhaus → registry.toph.so/kammer
- Update workflow to build and push kammer image
- Update package names and metadata

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-27 01:24:15 +01:00
a04672b4d0 docs: add Gherkin spec for short UID generation feature
Add comprehensive feature specification for item UID generation with:
- 7-character collision-resistant IDs using safe alphabet
- Pre-generation on form load with regenerate option
- UI/UX guidelines for monospace display and inline controls
- Edge case handling for collisions and persistence

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-27 00:48:04 +01:00
bfc2d4c312 fix: switch to nixos-unstable to avoid hash mismatch
nixos-25.11 has hash mismatch for bash-5.3.tar.gz upstream source.
Switch to nixos-unstable which has updated/fixed hashes.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-27 00:41:03 +01:00
96fb092f35 fix: add retry logic for flaky cache.nixos.org
Build can fail with EOF errors when fetching from cache.
Add fallback to build from source if cache fails.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-27 00:35:40 +01:00
4d3ab2e92f fix: create temp directory for skopeo in CI
skopeo needs /var/tmp or /tmp for temporary files when copying
docker-archive images. Use --tmpdir flag to explicitly use /tmp.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-27 00:31:50 +01:00
6de77d23b6 Merge pull request 'feat: PWA photo capture with IndexedDB storage' (#1) from feat/20260226-photo-capture into main 2026-02-26 23:27:01 +00:00
5 changed files with 192 additions and 23 deletions

View file

@ -16,30 +16,42 @@ jobs:
- name: Build OCI image
run: |
nix build .#solidhaus-image \
# Try with cache first, fall back to building from source
nix build .#kammer-image \
--print-build-logs \
--show-trace
--show-trace || {
echo "Build failed, retrying with --no-substitute to build from source..."
nix build .#kammer-image \
--print-build-logs \
--show-trace \
--option substitute false
}
- name: Push to registry
if: github.ref == 'refs/heads/main'
run: |
image=$(nix build --no-link --print-out-paths .#solidhaus-image)
# Ensure temp directory exists for skopeo
mkdir -p /var/tmp /tmp
image=$(nix build --no-link --print-out-paths .#kammer-image)
skopeo copy \
--dest-tls-verify=false \
--tmpdir /tmp \
"docker-archive:$image" \
"docker://registry.toph.so/solidhaus:latest"
"docker://registry.toph.so/kammer:latest"
# Also tag with commit SHA
skopeo copy \
--dest-tls-verify=false \
--tmpdir /tmp \
"docker-archive:$image" \
"docker://registry.toph.so/solidhaus:${GITHUB_SHA:0:7}"
"docker://registry.toph.so/kammer:${GITHUB_SHA:0:7}"
- name: Build summary
if: github.ref == 'refs/heads/main'
run: |
echo "### ✅ Image Built and Pushed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Image**: registry.toph.so/solidhaus:latest" >> $GITHUB_STEP_SUMMARY
echo "- **Image**: registry.toph.so/kammer:latest" >> $GITHUB_STEP_SUMMARY
echo "- **Tag**: ${GITHUB_SHA:0:7}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit**: ${GITHUB_SHA}" >> $GITHUB_STEP_SUMMARY

View file

@ -0,0 +1,156 @@
Feature: Item UID Generation - Short Collision-Resistant IDs
As a user creating a new item
I want to see a pre-generated short UID (7 characters)
So that IDs remain compact, memorable, and shareable while avoiding collisions
Background:
Given I am on the "New Item" form
Rule: UIDs are pre-generated on form load
Scenario: UID is pre-generated when form loads
When the form loads
Then I should see a "UID" field
And the UID field should contain a 7-character ID
And the UID should use only characters from "23456789abcdefghjkmnpqrstuvwxyz"
And the UID should be displayed prominently near the top of the form
Scenario: UID field is read-only with regenerate option
When the form loads
Then the UID field should be read-only or disabled for manual editing
And I should see a "Regenerate" button/icon next to the UID field
And the regenerate control should be clearly clickable
Rule: Users can regenerate UIDs before submission
Scenario: User regenerates UID once
Given the form has loaded with UID "qe3saa3"
When I click the "Regenerate" button
Then a new 7-character UID should be generated
And the new UID should be different from "qe3saa3"
And the new UID should be displayed in the UID field immediately
Scenario: User regenerates UID multiple times
Given the form has loaded with UID "qe3saa3"
When I click the "Regenerate" button
And the new UID is "n4p7m2k"
And I click the "Regenerate" button again
Then another new 7-character UID should be generated
And each generated UID should be unique
And the latest UID should replace the previous one
Scenario: Regenerated UID is used on submission
Given the form has loaded with UID "qe3saa3"
And I fill in "Name" with "Cable Drum"
When I click the "Regenerate" button
And the new UID is "n4p7m2k"
And I submit the form
Then the item should be created with shortId "n4p7m2k"
And the barcode URI should be "https://haus.toph.so/n4p7m2k"
And the original UID "qe3saa3" should NOT be used
Rule: UID format and collision resistance
Scenario: UID uses safe alphabet
When a UID is generated
Then it should use alphabet "23456789abcdefghjkmnpqrstuvwxyz"
And it should exclude confusing characters (0, 1, i, l, o)
And it should be exactly 7 characters long
And all characters should be lowercase
Scenario: Collision probability is negligible
Given the alphabet has 29 characters (excluding 0, 1, i, l, o)
And the UID length is 7 characters
Then the total possible IDs should be 29^7 = 17,249,876,309
And collision probability for 1,000 items should be approximately 0.000003%
And collision probability for 10,000 items should be approximately 0.0003%
And collision probability for 100,000 items should be approximately 0.03%
Scenario Outline: UID examples are valid
When a UID "<uid>" is generated
Then it should match the pattern "^[23456789a-hjkmnp-z]{7}$"
And it should NOT contain any of "0 1 i l o"
Examples:
| uid |
| qe3saa3 |
| n4p7m2k |
| 2jk8xab |
| zzz9999 |
| 2222222 |
Rule: UID field UI/UX specifications
Scenario: UID field placement
When the form loads
Then the UID field should be positioned near the top
And it should appear before or after the "Name" field
And it should be clearly labeled "UID" or "Item ID"
Scenario: UID field styling
When the form loads
Then the UID field should use monospace font
And it should have larger text size for readability
And it should have letter-spacing for clarity
And the field should visually indicate it's read-only
Scenario: Regenerate button styling
When the form loads
Then the "Regenerate" button should be compact
And it should use an icon (🔄 or ) or short text
And it should be positioned inline with the UID field (right side)
And it should have a clear hover state
And it should have appropriate spacing from the UID field
Scenario: Regenerate button interaction
When I hover over the "Regenerate" button
Then it should show a visual hover state
And optionally show a tooltip "Generate new ID"
When I click the "Regenerate" button
Then the button should show brief loading/animation feedback
And the new UID should appear immediately
Rule: UID persistence and submission
Scenario: UID is submitted with item
Given the form has pre-generated UID "qe3saa3"
And I fill in "Name" with "Laptop"
And I fill in "Category" with "Electronics"
When I submit the form
Then the item should be created with shortId "qe3saa3"
And the database should store shortId as primary key
And the item detail page should display "qe3saa3"
And the barcode should encode "https://haus.toph.so/qe3saa3"
Scenario: Creation timestamp stored separately
Given the form has pre-generated UID "qe3saa3"
When I submit the form at 2024-02-27T14:30:00Z
Then the item should have shortId "qe3saa3"
And the item should have createdAt "2024-02-27T14:30:00Z"
And the item should have updatedAt "2024-02-27T14:30:00Z"
And timestamps should be independent of the UID
Rule: Edge cases and validation
Scenario: Duplicate UID is detected (rare collision)
Given an item exists with UID "qe3saa3"
And the form generates the same UID "qe3saa3" by chance
When I submit the form
Then the system should detect the collision
And the system should automatically generate a new UID
And the item should be created successfully with a different UID
And the user should be notified "ID regenerated due to conflict"
Scenario: UID persists during form edits
Given the form has loaded with UID "qe3saa3"
When I fill in "Name" with "Cable"
And I change "Name" to "Cable Drum"
And I fill in other fields
Then the UID should remain "qe3saa3"
And the UID should NOT auto-regenerate on field changes
Scenario: UID regenerates on explicit action only
Given the form has loaded with UID "qe3saa3"
When I interact with any form field
Then the UID should remain unchanged
And the UID should only change when "Regenerate" is clicked

8
flake.lock generated
View file

@ -38,16 +38,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1771903837,
"narHash": "sha256-sdaqdnsQCv3iifzxwB22tUwN/fSHoN7j2myFW5EIkGk=",
"lastModified": 1771848320,
"narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951",
"rev": "2fc6539b481e1d2569f25f8799236694180c0993",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.11",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}

View file

@ -1,8 +1,8 @@
{
description = "SolidHaus Local-first household inventory app";
description = "Kammer Local-first household inventory app";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
sd-card.url = "git+ssh://git@git.toph.so/toph/sd-card";
};
@ -29,8 +29,8 @@
in {
packages = {
# Build the SvelteKit app
solidhaus = buildNpmPackage {
pname = "solidhaus";
kammer = buildNpmPackage {
pname = "kammer";
version = "0.0.1";
src = ./.;
@ -48,13 +48,13 @@
meta = {
description = "Local-first household inventory app with barcode scanning";
homepage = "https://git.toph.so/toph/solidhaus";
homepage = "https://git.toph.so/toph/kammer";
};
};
# OCI image with nginx serving the built app
solidhaus-image = pkgs.dockerTools.buildLayeredImage {
name = "registry.toph.so/solidhaus";
kammer-image = pkgs.dockerTools.buildLayeredImage {
name = "registry.toph.so/kammer";
tag = "latest";
contents = with pkgs; [
@ -85,7 +85,7 @@
# Copy built app
mkdir -p usr/share/nginx/html
cp -r ${config.packages.solidhaus}/* usr/share/nginx/html/
cp -r ${config.packages.kammer}/* usr/share/nginx/html/
# Create nginx config
cat > etc/nginx/nginx.conf <<'EOF'
@ -147,22 +147,22 @@
devPort = 5173; # Vite default port
};
default = config.packages.solidhaus;
default = config.packages.kammer;
};
apps = {
# Push image to registry
push-solidhaus-image = {
push-kammer-image = {
type = "app";
program = pkgs.lib.getExe (pkgs.writeShellApplication {
name = "push-solidhaus-image";
name = "push-kammer-image";
runtimeInputs = [pkgs.skopeo];
text = ''
image=$(nix build --no-link --print-out-paths .#solidhaus-image)
image=$(nix build --no-link --print-out-paths .#kammer-image)
skopeo copy \
--insecure-policy \
"docker-archive:$image" \
"docker://registry.toph.so/solidhaus:latest"
"docker://registry.toph.so/kammer:latest"
'';
});
};

1
result Symbolic link
View file

@ -0,0 +1 @@
/nix/store/3dna41ydmr1ia5fiicgzf19p1z68l10v-solidhaus.tar.gz